From dd80e8b7990db059ba8dc2c7d200eb6cd8fb90bf Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Fri, 20 Jun 2025 23:31:22 +0200 Subject: [PATCH] Compile time type assignment (eg `$Foo = int`) is no longer an expression. --- releasenotes.md | 1 + src/compiler/c_codegen.c | 1 + src/compiler/compiler_internal.h | 7 +++++++ src/compiler/copying.c | 3 +++ src/compiler/enums.h | 3 ++- src/compiler/parse_stmt.c | 18 ++++++++++++++++-- src/compiler/sema_decls.c | 3 ++- src/compiler/sema_expr.c | 32 -------------------------------- src/compiler/sema_stmts.c | 20 ++++++++++++++++++++ 9 files changed, 52 insertions(+), 36 deletions(-) diff --git a/releasenotes.md b/releasenotes.md index 36508e120..0755711b6 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -24,6 +24,7 @@ - Allow generics over distinct types #2216. - Support distrinct types as the base type of bitstructs. #2218 - Add hash::sha512 module to stdlib. #2227 +- Compile time type assignment (eg `$Foo = int`) is no longer an expression. ### Fixes - `-2147483648`, MIN literals work correctly. diff --git a/src/compiler/c_codegen.c b/src/compiler/c_codegen.c index c27a15dcd..22ee909ed 100644 --- a/src/compiler/c_codegen.c +++ b/src/compiler/c_codegen.c @@ -772,6 +772,7 @@ static void c_emit_stmt(GenContext *c, Ast *stmt) case AST_CT_FOR_STMT: case AST_CT_IF_STMT: case AST_CT_SWITCH_STMT: + case AST_CT_TYPE_ASSIGN_STMT: UNREACHABLE case AST_DECLARE_STMT: { diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 418e2ce3d..aaf3f1344 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -1359,6 +1359,12 @@ typedef struct } AstCtSwitchStmt; +typedef struct +{ + const char *var_name; + Expr *type_expr; +} AstCtTypeAssignStmt; + typedef struct { DeclId index; @@ -1500,6 +1506,7 @@ typedef struct Ast_ AstContractStmt contract_stmt; // 32 AstDocFault contract_fault; // 24 AstId ct_else_stmt; // 4 + AstCtTypeAssignStmt ct_type_assign_stmt; AstCtForeachStmt ct_foreach_stmt; // 40 AstCtIfStmt ct_if_stmt; // 24 AstCtSwitchStmt ct_switch_stmt; // 16 diff --git a/src/compiler/copying.c b/src/compiler/copying.c index b06b17148..77c1e776f 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -643,6 +643,9 @@ RETRY: { case AST_POISONED: break; + case AST_CT_TYPE_ASSIGN_STMT: + MACRO_COPY_EXPR(ast->ct_type_assign_stmt.type_expr); + break; case AST_DECLS_STMT: MACRO_COPY_DECL_LIST(ast->decls_stmt); break; diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 57f3f9cf6..d267e46c4 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -208,6 +208,7 @@ typedef enum AST_CT_FOR_STMT, AST_CT_IF_STMT, AST_CT_SWITCH_STMT, + AST_CT_TYPE_ASSIGN_STMT, AST_DECLARE_STMT, AST_DECLS_STMT, AST_DEFAULT_STMT, @@ -1629,7 +1630,7 @@ typedef enum #define CT_AST \ AST_CT_ASSERT: case AST_CT_ECHO_STMT: case AST_CT_ELSE_STMT: \ case AST_CT_FOREACH_STMT: case AST_CT_FOR_STMT: \ - case AST_CT_IF_STMT: case AST_CT_SWITCH_STMT + case AST_CT_IF_STMT: case AST_CT_SWITCH_STMT: case AST_CT_TYPE_ASSIGN_STMT // -- Decl helper macros #define NON_TYPE_DECLS DECL_IMPORT: case DECL_MACRO: \ diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index bf5698c01..192133390 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -965,9 +965,23 @@ static inline Ast *parse_expr_stmt(ParseContext *c) } +static inline Ast *parse_ct_type_assign_stmt(ParseContext *c) +{ + Ast *stmt = new_ast(AST_CT_TYPE_ASSIGN_STMT, c->span); + stmt->ct_type_assign_stmt.var_name = symstr(c); + advance_and_verify(c, TOKEN_CT_TYPE_IDENT); + advance_and_verify(c, TOKEN_EQ); + ASSIGN_EXPR_OR_RET(stmt->ct_type_assign_stmt.type_expr, parse_expr(c), poisoned_ast); + CONSUME_EOS_OR_RET(poisoned_ast); + return stmt; +} static inline Ast *parse_decl_or_expr_stmt(ParseContext *c) { + if (tok_is(c, TOKEN_CT_TYPE_IDENT) && peek(c) == TOKEN_EQ) + { + return parse_ct_type_assign_stmt(c); + } ASSIGN_EXPR_OR_RET(Expr *expr, parse_expr(c), poisoned_ast); // We might be parsing "int!" // If so we need to unwrap this. @@ -1275,9 +1289,9 @@ Ast *parse_stmt(ParseContext *c) return parse_decl_or_expr_stmt(c); case TOKEN_VAR: return parse_var_stmt(c); - case TOKEN_TLOCAL: // Global means declaration! + case TOKEN_TLOCAL: // Global means declaration! case TOKEN_STATIC: // Static means declaration! - case TOKEN_CONST: // Const means declaration! + case TOKEN_CONST: // Const means declaration! return parse_declaration_stmt(c); case TOKEN_RETURN: return parse_return_stmt(c); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 8b404f5ac..c8b8fe66d 100755 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -4172,8 +4172,9 @@ static inline bool sema_check_body_const(SemaContext *context, Ast *body) case AST_CT_FOREACH_STMT: case AST_CT_FOR_STMT: case AST_CT_IF_STMT: - case AST_CT_SWITCH_STMT: case AST_CT_ELSE_STMT: + case AST_CT_SWITCH_STMT: + case AST_CT_TYPE_ASSIGN_STMT: case AST_DECLARE_STMT: case AST_DECLS_STMT: case AST_NOP_STMT: diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 7eda1dca9..391cfb69b 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -93,7 +93,6 @@ static bool sema_expr_check_shift_rhs(SemaContext *context, Expr *expr, Type *le static bool sema_expr_analyse_and_or(SemaContext *context, Expr *expr, Expr *left, Expr *right, bool *failed_ref); static bool sema_expr_analyse_slice_assign(SemaContext *context, Expr *expr, Type *left_type, Expr *right, bool *failed_ref); static bool sema_expr_analyse_ct_identifier_assign(SemaContext *context, Expr *expr, Expr *left, Expr *right); -static bool sema_expr_analyse_ct_type_identifier_assign(SemaContext *context, Expr *expr, Expr *left, Expr *right); static bool sema_expr_analyse_assign(SemaContext *context, Expr *expr, Expr *left, Expr *right, bool *failed_ref); static bool sema_expr_analyse_comp(SemaContext *context, Expr *expr, Expr *left, Expr *right, bool *failed_ref); static bool sema_expr_analyse_op_assign(SemaContext *context, Expr *expr, Expr *left, Expr *right, BinaryOp operator); @@ -6364,30 +6363,6 @@ static bool sema_expr_analyse_ct_subscript_assign(SemaContext *context, Expr *ex return true; } -static bool sema_expr_analyse_ct_type_identifier_assign(SemaContext *context, Expr *expr, Expr *left, Expr *right) -{ - TypeInfo *info = left->type_expr; - if (info->kind != TYPE_INFO_CT_IDENTIFIER) - { - RETURN_SEMA_ERROR(left, "A type cannot be assigned to."); - } - - if (!sema_analyse_expr_value(context, right)) return false; - if (right->expr_kind == EXPR_TYPEINFO) - { - expr_rewrite_const_typeid(right, right->type_expr->type); - } - if (!expr_is_const_typeid(right)) RETURN_SEMA_ERROR(right, "Expected a type or constant typeid here."); - - Decl *decl = sema_find_symbol(context, info->unresolved.name); - if (!decl) RETURN_SEMA_ERROR(info, "'%s' is not defined in this scope yet.", info->unresolved.name); - - decl->var.init_expr = right; - expr->expr_kind = EXPR_NOP; - expr->type = type_void; - - return true; -} static bool sema_expr_fold_hash(SemaContext *context, Expr *expr) { @@ -6414,13 +6389,6 @@ static bool sema_expr_fold_hash(SemaContext *context, Expr *expr) */ static bool sema_expr_analyse_assign(SemaContext *context, Expr *expr, Expr *left, Expr *right, bool *failed_ref) { - // 1. Evaluate left side - if (left->expr_kind == EXPR_TYPEINFO) - { - // Later make sure this can be handled in lvalue - // $Foo = ... - return sema_expr_analyse_ct_type_identifier_assign(context, expr, left, right); - } if (!sema_analyse_expr_lvalue(context, left, failed_ref)) return false; switch (left->expr_kind) { diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index ac5841879..101a2f2dd 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -1187,6 +1187,24 @@ static inline bool sema_analyse_cond(SemaContext *context, Expr *expr, CondType return true; } +static inline bool sema_analyse_ct_type_assign_stmt(SemaContext *context, Ast *statement) +{ + Expr *right = statement->ct_type_assign_stmt.type_expr; + if (!sema_analyse_expr_value(context, right)) return false; + if (right->expr_kind == EXPR_TYPEINFO) + { + expr_rewrite_const_typeid(right, right->type_expr->type); + } + if (!expr_is_const_typeid(right)) RETURN_SEMA_ERROR(right, "Expected a type or constant typeid here."); + + Decl *decl = sema_find_symbol(context, statement->ct_type_assign_stmt.var_name); + if (!decl) RETURN_SEMA_ERROR(statement, "'%s' is not defined in this scope yet.", statement->ct_type_assign_stmt.var_name); + + decl->var.init_expr = right; + statement->ast_kind = AST_NOP_STMT; + + return true; +} static inline bool sema_analyse_decls_stmt(SemaContext *context, Ast *statement) { @@ -3044,6 +3062,8 @@ static inline bool sema_analyse_statement_inner(SemaContext *context, Ast *state case AST_ASM_LABEL: case AST_CONTRACT_FAULT: UNREACHABLE + case AST_CT_TYPE_ASSIGN_STMT: + return sema_analyse_ct_type_assign_stmt(context, statement); case AST_DECLS_STMT: return sema_analyse_decls_stmt(context, statement); case AST_ASM_BLOCK_STMT: