From 4222f2731ec15d8e3771568e8ea4a5c42a8fdbb5 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Wed, 14 Oct 2020 12:26:27 +0200 Subject: [PATCH] &&temp operator. Macro evaluation. --- resources/testfragments/file1.c3 | 53 +- resources/testfragments/file2.c3 | 5 +- src/compiler/ast.c | 56 +- src/compiler/compiler_internal.h | 44 +- src/compiler/enums.h | 30 +- src/compiler/lexer.c | 7 +- src/compiler/llvm_codegen.c | 22 +- src/compiler/llvm_codegen_expr.c | 47 +- src/compiler/llvm_codegen_function.c | 24 +- src/compiler/llvm_codegen_internal.h | 2 +- src/compiler/llvm_codegen_stmt.c | 20 +- src/compiler/number.c | 6 - src/compiler/parse_expr.c | 44 +- src/compiler/parse_global.c | 113 ++- src/compiler/parse_stmt.c | 83 +- src/compiler/parser_internal.h | 2 +- src/compiler/sema_casts.c | 5 - src/compiler/sema_decls.c | 56 +- src/compiler/sema_expr.c | 897 ++++++++++++++++-- src/compiler/sema_name_resolution.c | 1 + src/compiler/sema_passes.c | 4 + src/compiler/sema_stmts.c | 90 +- src/compiler/symtab.c | 2 + src/compiler/tokens.c | 10 + src/compiler/types.c | 4 +- test/test_suite/constants/constants.c3t | 15 +- test/test_suite/expressions/call_arg_types.c3 | 4 +- test/test_suite/globals/misplaced_const.c3 | 8 +- test/test_suite/symbols/various.c3 | 26 +- 29 files changed, 1332 insertions(+), 348 deletions(-) diff --git a/resources/testfragments/file1.c3 b/resources/testfragments/file1.c3 index 756c62c1d..355e8ed3f 100644 --- a/resources/testfragments/file1.c3 +++ b/resources/testfragments/file1.c3 @@ -4,60 +4,15 @@ import bar; struct Foo { int x; - long z; - int y; - struct fe - { - int dd; - int ee; - } -} -struct Bar -{ - int[1024] x; int y; } -public func Bar barCopy(Bar *bar) -{ - Bar copy = *bar; - copy.x[0] = copy.y; - return copy; -} + + //define bar::blub(Foo, 1) as fooblub; -extern func int blurbi(); -extern func int oefk(int i); - public func void main() { - Foo f = {}; - usize x = Foo.sizeof; - usize y = int.sizeof; - usize z = int.alignof; - usize w = Foo.alignof; - typeid t = Foo.x.typeid; - typeid aa = Foo.typeid; - typeid bb = int.typeid; - typeid cc = Foo.fe.typeid; - typeid dd = Foo.fe.dd.typeid; - char* str = Foo.nameof; - char* str2 = int.nameof; - char* str3 = Foo.qnameof; - char* str4 = int.qnameof; - char* foo = (int*).nameof; - int blurb = void; - Foo foek = { x = blurbi() }; - Foo floed = void; - if (foek.x != 2) - { - oefk(floed.x); - } - else - { - oefk(foek.x); - } - //int fe = Foo.x.offsetof; - // usize vv = Foo.x.offsetof; - // f = fooblud(f); + Foo f = { 3, 4 }; + //Foo g = fooblub(&f); } \ No newline at end of file diff --git a/resources/testfragments/file2.c3 b/resources/testfragments/file2.c3 index f4d602f18..95a078c2f 100644 --- a/resources/testfragments/file2.c3 +++ b/resources/testfragments/file2.c3 @@ -1,10 +1,7 @@ -module bar; - -struct Type { int x; } +module bar(Type, i); public func Type blub(Type type) { -int i = 0; type.x = i; return type; } \ No newline at end of file diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 23a45794b..ca7e9888c 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -115,8 +115,6 @@ const char *decl_var_to_string(VarDeclKind kind) { case VARDECL_CONST: return "const"; - case VARDECL_CONST_CT: - return "$const"; case VARDECL_GLOBAL: return "global"; case VARDECL_LOCAL: @@ -127,6 +125,14 @@ const char *decl_var_to_string(VarDeclKind kind) return "param"; case VARDECL_ALIAS: return "alias"; + case VARDECL_PARAM_CT: + return "$param"; + case VARDECL_PARAM_CT_TYPE: + return "$Param"; + case VARDECL_PARAM_EXPR: + return "#param"; + case VARDECL_PARAM_REF: + return "¶m"; case VARDECL_LOCAL_CT: return "$local"; case VARDECL_LOCAL_CT_TYPE: @@ -222,6 +228,7 @@ BinaryOp binaryop_assign_base_op(BinaryOp assign_binary_op) UnaryOp unary_op[TOKEN_LAST + 1] = { [TOKEN_STAR] = UNARYOP_DEREF, [TOKEN_AMP] = UNARYOP_ADDR, + [TOKEN_AND] = UNARYOP_TADDR, [TOKEN_BIT_NOT] = UNARYOP_BITNEG, [TOKEN_BANG] = UNARYOP_NOT, [TOKEN_MINUS] = UNARYOP_NEG, @@ -495,9 +502,15 @@ void fprint_expr_recursive(Context *context, FILE *file, Expr *expr, int indent) if (!expr) return; switch (expr->expr_kind) { + case EXPR_MEMBER_ACCESS: + DUMP("(member access)"); + return; case EXPR_UNDEF: DUMP("(undef)"); return; + case EXPR_ENUM_CONSTANT: + DUMP("(enumconstant)"); + return; case EXPR_TYPEINFO: TODO; case EXPR_SLICE_ASSIGN: @@ -520,15 +533,24 @@ void fprint_expr_recursive(Context *context, FILE *file, Expr *expr, int indent) DUMPEXPC(expr); DUMPEXPR(expr->failable_expr); DUMPEND(); + case EXPR_MACRO_IDENTIFIER: + DUMPF("(ident @%s", expr->macro_identifier_expr.identifier); + DUMPEXPC(expr); + DUMPEND(); case EXPR_IDENTIFIER: - if (expr->identifier_expr.is_macro) - { - DUMPF("(ident @%s", expr->identifier_expr.identifier); - } - else - { - DUMPF("(ident %s", expr->identifier_expr.identifier); - } + DUMPF("(ident %s", expr->identifier_expr.identifier); + DUMPEXPC(expr); + DUMPEND(); + case EXPR_CT_IDENT: + DUMPF("(ctident %s", expr->ct_ident_expr.identifier); + DUMPEXPC(expr); + DUMPEND(); + case EXPR_MACRO_CT_IDENTIFIER: + DUMPF("(macroctident @%s", expr->ct_ident_expr.identifier); + DUMPEXPC(expr); + DUMPEND(); + case EXPR_CONST_IDENTIFIER: + DUMPF("(ident %s", expr->identifier_expr.identifier); DUMPEXPC(expr); DUMPEND(); case EXPR_MACRO_BLOCK: @@ -750,15 +772,18 @@ void fprint_decl_recursive(Context *context, FILE *file, Decl *decl, int indent) DUMPTI(decl->var.type_info); switch (decl->var.kind) { - case VARDECL_CONST_CT: case VARDECL_CONST: case VARDECL_GLOBAL: case VARDECL_LOCAL: case VARDECL_PARAM: + case VARDECL_PARAM_CT: + case VARDECL_PARAM_EXPR: + case VARDECL_PARAM_REF: case VARDECL_MEMBER: case VARDECL_LOCAL_CT: DUMPEXPR(decl->var.init_expr); break; + case VARDECL_PARAM_CT_TYPE: case VARDECL_LOCAL_CT_TYPE: DUMPTI(decl->var.type_info); break; @@ -979,6 +1004,15 @@ static void fprint_ast_recursive(Context *context, FILE *file, Ast *ast, int ind DUMP("(compound\n"); fprint_asts_recursive(context, file, ast->compound_stmt.stmts, indent + 1); DUMPEND(); + case AST_CT_COMPOUND_STMT: + if (!ast->compound_stmt.stmts) + { + DUMP("(ct-compound)"); + return; + } + DUMP("(ct-compound\n"); + fprint_asts_recursive(context, file, ast->ct_compound_stmt, indent + 1); + DUMPEND(); case AST_DEFINE_STMT: DUMP("(define"); DUMPDECL(ast->define_stmt); diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 00564fffd..dacf312f6 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -284,7 +284,11 @@ typedef struct _VarDecl Expr *init_expr; Decl *alias; }; - void *backend_debug_ref; + union + { + void *backend_debug_ref; + void *scope; + }; void *failable_ref; } VarDecl; @@ -425,7 +429,8 @@ typedef struct _Decl Visibility visibility : 2; ResolveStatus resolve_status : 2; bool is_packed : 1; - void *ref; + bool has_addr : 1; + void *backend_ref; const char *cname; uint32_t alignment; const char *section; @@ -573,11 +578,26 @@ typedef struct { Path *path; const char *identifier; - bool is_ref; - bool is_macro; + bool is_ref : 1; + bool is_rvalue : 1; Decl *decl; } ExprIdentifier; +typedef struct +{ + const char *identifier; + bool is_ref : 1; + bool is_rvalue : 1; + Decl *decl; +} ExprIdentifierRaw; + +typedef struct +{ + const char *identifier; + bool is_macro; + Decl *decl; +} ExprCtIdentifier; + typedef struct { @@ -689,8 +709,12 @@ struct _Expr ExprSubscript subscript_expr; ExprAccess access_expr; ExprIdentifier identifier_expr; + ExprIdentifier macro_identifier_expr; + ExprIdentifierRaw ct_ident_expr; + ExprIdentifierRaw ct_macro_ident_expr; TypeInfo *typeid_expr; ExprInitializer expr_initializer; + Decl *expr_enum; ExprCompoundLiteral expr_compound_literal; Expr** expression_list; ExprScope expr_scope; @@ -946,6 +970,7 @@ typedef struct _Ast FlowCommon flow; // Shared struct AstAsmStmt asm_stmt; // 24 AstCompoundStmt compound_stmt; // 16 + Ast** ct_compound_stmt; Decl *declare_stmt; // 8 Expr *expr_stmt; // 8 AstTryStmt try_stmt; @@ -1138,7 +1163,7 @@ extern Type *type_byte, *type_ushort, *type_uint, *type_ulong, *type_usize; extern Type *type_compint, *type_compfloat; extern Type *type_c_short, *type_c_int, *type_c_long, *type_c_longlong; extern Type *type_c_ushort, *type_c_uint, *type_c_ulong, *type_c_ulonglong; -extern Type *type_typeid, *type_error, *type_typeinfo, *type_member; +extern Type *type_typeid, *type_error, *type_typeinfo; extern const char *attribute_list[NUMBER_OF_ATTRIBUTES]; @@ -1150,6 +1175,7 @@ extern const char *kw_kindof; extern const char *kw_nameof; extern const char *kw_qnameof; extern const char *kw_len; +extern const char *kw_ordinal; #define AST_NEW_TOKEN(_kind, _token) new_ast(_kind, source_span_from_token_id(_token.id)) #define AST_NEW(_kind, _loc) new_ast(_kind, _loc) @@ -1240,6 +1266,7 @@ bool sema_expr_analyse_assign_right_side(Context *context, Expr *expr, Type *lef bool sema_analyse_expr_of_required_type(Context *context, Type *to, Expr *expr, bool may_be_failable); bool sema_analyse_expr(Context *context, Type *to, Expr *expr); bool sema_analyse_decl(Context *context, Decl *decl); +bool expr_is_constant_eval(Expr *expr); void compiler_add_type(Type *type); Decl *compiler_find_symbol(const char *name); @@ -1440,7 +1467,8 @@ unsigned int type_abi_alignment(Type *type); const char *type_generate_qname(Type *type); void type_append_signature_name(Type *type, char *dst, size_t *offset); Type *type_find_max_type(Type *type, Type *other); -static inline bool type_is_builtin(TypeKind kind) { return kind >= TYPE_VOID && kind <= TYPE_FXX; } + +static inline bool type_is_builtin(TypeKind kind) { return kind >= TYPE_VOID && kind <= TYPE_TYPEID; } static inline bool type_kind_is_signed(TypeKind kind) { return kind >= TYPE_I8 && kind <= TYPE_I64; } static inline bool type_kind_is_unsigned(TypeKind kind) { return kind >= TYPE_U8 && kind <= TYPE_U64; } static inline bool type_kind_is_any_integer(TypeKind kind) { return kind >= TYPE_I8 && kind <= TYPE_IXX; } @@ -1625,4 +1653,6 @@ static inline void advance_and_verify(Context *context, TokenType token_type) #define TRY_TYPE_REAL_OR(_type_stmt, _res) ({ Type* _type = (_type_stmt); if (!type_ok(_type)) return _res; _type; }) #define TRY_DECL_OR(_decl_stmt, _res) ({ Decl* _decl = (_decl_stmt); if (!decl_ok(_decl)) return _res; _decl; }) -#pragma clang diagnostic pop \ No newline at end of file +#pragma clang diagnostic pop + + diff --git a/src/compiler/enums.h b/src/compiler/enums.h index fef57c0b6..1dca4404b 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -59,6 +59,7 @@ typedef enum AST_CONTINUE_STMT, AST_DEFINE_STMT, AST_CT_ASSERT, + AST_CT_COMPOUND_STMT, AST_CT_IF_STMT, AST_CT_ELIF_STMT, AST_CT_ELSE_STMT, @@ -184,6 +185,10 @@ typedef enum EXPR_POST_UNARY, EXPR_TYPEID, EXPR_IDENTIFIER, + EXPR_MACRO_IDENTIFIER, + EXPR_CT_IDENT, + EXPR_CONST_IDENTIFIER, + EXPR_MACRO_CT_IDENTIFIER, EXPR_CALL, EXPR_GROUP, EXPR_SUBSCRIPT, @@ -195,6 +200,7 @@ typedef enum EXPR_CAST, EXPR_TYPEINFO, EXPR_TYPEOF, + EXPR_MEMBER_ACCESS, EXPR_SCOPED_EXPR, EXPR_EXPR_BLOCK, EXPR_MACRO_BLOCK, @@ -204,6 +210,7 @@ typedef enum EXPR_DECL_LIST, EXPR_LEN, EXPR_UNDEF, + EXPR_ENUM_CONSTANT, } ExprKind; typedef enum @@ -374,6 +381,11 @@ typedef enum TOKEN_CT_CONST_IDENT, // $FOOBAR TOKEN_CT_TYPE_IDENT, // $Foobar + // We want to parse #foo separately. + TOKEN_HASH_IDENT, // #foobar + TOKEN_HASH_CONST_IDENT, // #FOOBAR + TOKEN_HASH_TYPE_IDENT, // #Foobar + TOKEN_STRING, // "Teststring" TOKEN_INTEGER, // 123 0x23 0b10010 0o327 TOKEN_CHAR_LITERAL, // 'a' 'FO' 'BARS' '\u1232' @@ -433,6 +445,8 @@ typedef enum TOKEN_CT_FOR, // $for TOKEN_CT_ELIF, // $elif TOKEN_CT_ELSE, // $else + TOKEN_CT_ENDIF, // $endif + TOKEN_CT_ENDSWITCH, // $endswitch TOKEN_CT_IF, // $if TOKEN_CT_SWITCH, // $switch TOKEN_CT_UNREACHABLE, // $unreachable @@ -469,6 +483,7 @@ typedef enum TYPE_F32, TYPE_F64, TYPE_FXX, + TYPE_TYPEID, TYPE_POINTER, TYPE_ENUM, TYPE_FUNC, @@ -483,8 +498,7 @@ typedef enum TYPE_SUBARRAY, TYPE_TYPEINFO, TYPE_MEMBER, - TYPE_TYPEID, - TYPE_LAST = TYPE_TYPEID + TYPE_LAST = TYPE_MEMBER } TypeKind; #define ALL_INTS TYPE_I8: case TYPE_I16: case TYPE_I32: case TYPE_I64: \ @@ -506,6 +520,7 @@ typedef enum UNARYOP_NOT, UNARYOP_INC, UNARYOP_DEC, + UNARYOP_TADDR, UNARYOP_LAST = UNARYOP_DEC } UnaryOp; @@ -522,10 +537,13 @@ typedef enum VARDECL_LOCAL = 2, VARDECL_PARAM = 3, VARDECL_MEMBER = 4, - VARDECL_LOCAL_CT = 5, - VARDECL_LOCAL_CT_TYPE = 6, - VARDECL_CONST_CT = 7, - VARDECL_ALIAS = 8, + VARDECL_PARAM_CT = 5, + VARDECL_PARAM_CT_TYPE = 6, + VARDECL_PARAM_REF = 7, + VARDECL_PARAM_EXPR = 8, + VARDECL_LOCAL_CT = 9, + VARDECL_LOCAL_CT_TYPE = 10, + VARDECL_ALIAS = 11, } VarDeclKind; typedef enum diff --git a/src/compiler/lexer.c b/src/compiler/lexer.c index d29bc2dd6..e365b7da2 100644 --- a/src/compiler/lexer.c +++ b/src/compiler/lexer.c @@ -745,7 +745,7 @@ static bool lexer_scan_token_inner(Lexer *lexer) case '"': return scan_string(lexer); case '#': - return add_token(lexer, TOKEN_HASH, "#"); + return scan_ident(lexer, TOKEN_HASH_IDENT, TOKEN_HASH_CONST_IDENT, TOKEN_HASH_TYPE_IDENT, '$'); case '$': return scan_ident(lexer, TOKEN_CT_IDENT, TOKEN_CT_CONST_IDENT, TOKEN_CT_TYPE_IDENT, '$'); case ',': @@ -915,5 +915,10 @@ bool lexer_scan_ident_test(Lexer *lexer, const char *scan) next(lexer); return scan_ident(lexer, TOKEN_CT_IDENT, TOKEN_CT_CONST_IDENT, TOKEN_CT_TYPE_IDENT, '$'); } + if (scan[0] == '#') + { + next(lexer); + return scan_ident(lexer, TOKEN_HASH_IDENT, TOKEN_HASH_CONST_IDENT, TOKEN_HASH_TYPE_IDENT, '#'); + } return scan_ident(lexer, TOKEN_IDENT, TOKEN_CONST_IDENT, TOKEN_TYPE_IDENT, 0); } diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index b97482291..c25eb6b54 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -60,35 +60,36 @@ LLVMValueRef gencontext_emit_memclear(GenContext *context, LLVMValueRef ref, Typ static void gencontext_emit_global_variable_definition(GenContext *context, Decl *decl) { - if (decl->var.kind == VARDECL_CONST_CT) return; - assert(decl->var.kind == VARDECL_GLOBAL || decl->var.kind == VARDECL_CONST); + // Skip real constants. + if (!decl->type) return; + // TODO fix name - decl->ref = LLVMAddGlobal(context->module, llvm_type(decl->type), decl->name); + decl->backend_ref = LLVMAddGlobal(context->module, llvm_type(decl->type), decl->name); if (decl->var.init_expr) { - LLVMSetInitializer(decl->ref, gencontext_emit_expr(context, decl->var.init_expr)); + LLVMSetInitializer(decl->backend_ref, gencontext_emit_expr(context, decl->var.init_expr)); } else { - LLVMSetInitializer(decl->ref, LLVMConstNull(llvm_type(decl->type))); + LLVMSetInitializer(decl->backend_ref, LLVMConstNull(llvm_type(decl->type))); } - LLVMSetGlobalConstant(decl->ref, decl->var.kind == VARDECL_CONST); + LLVMSetGlobalConstant(decl->backend_ref, decl->var.kind == VARDECL_CONST); switch (decl->visibility) { case VISIBLE_MODULE: - LLVMSetVisibility(decl->ref, LLVMProtectedVisibility); + LLVMSetVisibility(decl->backend_ref, LLVMProtectedVisibility); break; case VISIBLE_PUBLIC: - LLVMSetVisibility(decl->ref, LLVMDefaultVisibility); + LLVMSetVisibility(decl->backend_ref, LLVMDefaultVisibility); break; case VISIBLE_EXTERN: case VISIBLE_LOCAL: - LLVMSetVisibility(decl->ref, LLVMHiddenVisibility); + LLVMSetVisibility(decl->backend_ref, LLVMHiddenVisibility); break; } @@ -406,8 +407,7 @@ void llvm_codegen(Context *context) gencontext_destroy(&gen_context); } -void -gencontext_add_attribute(GenContext *context, LLVMValueRef value_to_add_attribute_to, unsigned attribute_id, int index) +void gencontext_add_attribute(GenContext *context, LLVMValueRef value_to_add_attribute_to, unsigned attribute_id, int index) { LLVMAttributeRef llvm_attr = LLVMCreateEnumAttribute(context->context, attribute_id, 0); LLVMAddAttributeAtIndex(value_to_add_attribute_to, index, llvm_attr); diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 6718a23ef..c9a2fc298 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -223,6 +223,12 @@ LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr) } switch (expr->expr_kind) { + case EXPR_MEMBER_ACCESS: + case EXPR_ENUM_CONSTANT: + case EXPR_CT_IDENT: + case EXPR_MACRO_CT_IDENTIFIER: + case EXPR_MACRO_IDENTIFIER: + UNREACHABLE case EXPR_DESIGNATED_INITIALIZER: // Should only appear when generating designated initializers. UNREACHABLE @@ -236,6 +242,7 @@ LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr) case EXPR_TYPEINFO: // Should never be an lvalue UNREACHABLE + case EXPR_CONST_IDENTIFIER: case EXPR_IDENTIFIER: return decl_ref(expr->identifier_expr.decl); case EXPR_UNARY: @@ -677,6 +684,13 @@ LLVMValueRef gencontext_emit_unary_expr(GenContext *context, Expr *expr) { case UNARYOP_ERROR: FATAL_ERROR("Illegal unary op %s", expr->unary_expr.operator); + case UNARYOP_TADDR: + { + LLVMValueRef val = gencontext_emit_expr(context, expr->unary_expr.expr); + LLVMValueRef temp = gencontext_emit_alloca(context, llvm_type(expr->unary_expr.expr->type), "taddr"); + LLVMBuildStore(context->builder, val, temp); + return temp; + } case UNARYOP_NOT: if (type_is_float(type)) { @@ -1661,14 +1675,14 @@ LLVMValueRef gencontext_emit_call_expr(GenContext *context, Expr *expr) { Decl *function_decl = expr->call_expr.function->access_expr.ref; signature = &function_decl->func.function_signature; - func = function_decl->ref; + func = function_decl->backend_ref; func_type = llvm_type(function_decl->type); } else { Decl *function_decl = expr->call_expr.function->identifier_expr.decl; signature = &function_decl->func.function_signature; - func = function_decl->ref; + func = function_decl->backend_ref; func_type = llvm_type(function_decl->type); } @@ -1792,9 +1806,26 @@ static inline LLVMValueRef gencontext_emit_macro_block(GenContext *context, Expr VECEACH(expr->macro_block.params, i) { // In case we have a constant, we never do an emit. The value is already folded. - if (!expr->macro_block.args[i]) continue; Decl *decl = expr->macro_block.params[i]; - decl->ref = gencontext_emit_alloca(context, llvm_type(decl->type), decl->name); + switch (decl->var.kind) + { + case VARDECL_CONST: + case VARDECL_GLOBAL: + case VARDECL_LOCAL: + case VARDECL_MEMBER: + case VARDECL_LOCAL_CT: + case VARDECL_LOCAL_CT_TYPE: + case VARDECL_ALIAS: + UNREACHABLE + case VARDECL_PARAM_REF: + case VARDECL_PARAM_CT: + case VARDECL_PARAM_CT_TYPE: + case VARDECL_PARAM_EXPR: + continue; + case VARDECL_PARAM: + break; + } + decl->backend_ref = gencontext_emit_alloca(context, llvm_type(decl->type), decl->name); LLVMValueRef value = gencontext_emit_expr(context, expr->macro_block.args[i]); gencontext_emit_store(context, decl, value); } @@ -1855,6 +1886,7 @@ LLVMValueRef gencontext_emit_assign_expr(GenContext *context, LLVMValueRef ref, static inline LLVMValueRef gencontext_emit_identifier_rvalue(GenContext *context, Decl *decl) { + if (decl->decl_kind != DECL_VAR || !decl->var.failable) { return gencontext_emit_load(context, decl->type, decl_ref(decl)); @@ -1902,9 +1934,14 @@ LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr) NESTED_RETRY: switch (expr->expr_kind) { + case EXPR_MEMBER_ACCESS: case EXPR_POISONED: case EXPR_DECL_LIST: case EXPR_TYPEINFO: + case EXPR_ENUM_CONSTANT: + case EXPR_MACRO_IDENTIFIER: + case EXPR_MACRO_CT_IDENTIFIER: + case EXPR_CT_IDENT: UNREACHABLE case EXPR_UNDEF: // Should never reach this. @@ -1954,6 +1991,8 @@ NESTED_RETRY: // These are folded in the semantic analysis step. UNREACHABLE case EXPR_IDENTIFIER: + case EXPR_CONST_IDENTIFIER: + assert(expr->identifier_expr.is_rvalue); return gencontext_emit_identifier_rvalue(context, expr->identifier_expr.decl); case EXPR_SUBSCRIPT: case EXPR_ACCESS: diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index 893df8ad7..83fec1ac1 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -76,7 +76,7 @@ static inline void gencontext_emit_parameter(GenContext *context, Decl *decl, un // Allocate room on stack and copy. const char *name = decl->name ? decl->name : "anon"; - decl->ref = gencontext_emit_alloca(context, llvm_type(decl->type), name); + decl->backend_ref = gencontext_emit_alloca(context, llvm_type(decl->type), name); if (gencontext_use_debug(context)) { SourceLocation *loc = TOKLOC(decl->span.loc); @@ -93,9 +93,9 @@ static inline void gencontext_emit_parameter(GenContext *context, Decl *decl, un ); decl->var.backend_debug_ref = var; LLVMDIBuilderInsertDeclareAtEnd(context->debug.builder, - decl->ref, var, LLVMDIBuilderCreateExpression(context->debug.builder, NULL, 0), - LLVMDIBuilderCreateDebugLocation(context->context, loc->line, loc->col, context->debug.function, /* inline at */NULL), - LLVMGetInsertBlock(context->builder)); + decl->backend_ref, var, LLVMDIBuilderCreateExpression(context->debug.builder, NULL, 0), + LLVMDIBuilderCreateDebugLocation(context->context, loc->line, loc->col, context->debug.function, /* inline at */NULL), + LLVMGetInsertBlock(context->builder)); } gencontext_emit_store(context, decl, LLVMGetParam(context->function, index)); @@ -121,7 +121,7 @@ void gencontext_emit_implicit_return(GenContext *context) void gencontext_emit_function_body(GenContext *context, Decl *decl) { DEBUG_LOG("Generating function %s.", decl->external_name); - assert(decl->ref); + assert(decl->backend_ref); bool emit_debug = gencontext_use_debug(context); LLVMValueRef prev_function = context->function; @@ -130,7 +130,7 @@ void gencontext_emit_function_body(GenContext *context, Decl *decl) context->error_var = NULL; context->catch_block = NULL; - context->function = decl->ref; + context->function = decl->backend_ref; if (emit_debug) { context->debug.function = LLVMGetSubprogram(context->function); @@ -217,7 +217,7 @@ void gencontext_emit_function_decl(GenContext *context, Decl *decl) assert(decl->decl_kind == DECL_FUNC); // Resolve function backend type for function. LLVMValueRef function = LLVMAddFunction(context->module, decl->cname ?: decl->external_name, llvm_type(decl->type)); - decl->ref = function; + decl->backend_ref = function; if (decl->func.function_signature.return_param) { if (!decl->func.function_signature.failable) @@ -314,13 +314,13 @@ void gencontext_emit_extern_decl(GenContext *context, Decl *decl) case DECL_POISONED: UNREACHABLE; case DECL_FUNC: - decl->ref = LLVMAddFunction(context->module, decl->cname ?: decl->external_name, - llvm_type(decl->type)); - LLVMSetVisibility(decl->ref, LLVMDefaultVisibility); + decl->backend_ref = LLVMAddFunction(context->module, decl->cname ?: decl->external_name, + llvm_type(decl->type)); + LLVMSetVisibility(decl->backend_ref, LLVMDefaultVisibility); break; case DECL_VAR: - decl->ref = LLVMAddGlobal(context->module, llvm_type(decl->type), decl->cname ?: decl->external_name); - LLVMSetVisibility(decl->ref, LLVMDefaultVisibility); + decl->backend_ref = LLVMAddGlobal(context->module, llvm_type(decl->type), decl->cname ?: decl->external_name); + LLVMSetVisibility(decl->backend_ref, LLVMDefaultVisibility); break; case DECL_TYPEDEF: UNREACHABLE diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index d6d2ecf85..42f70d68e 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -199,7 +199,7 @@ static inline LLVMValueRef decl_failable_ref(Decl *decl) static inline LLVMValueRef decl_ref(Decl *decl) { if (decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_ALIAS) return decl_ref(decl->var.alias); - return decl->ref; + return decl->backend_ref; } static inline void gencontext_emit_store(GenContext *context, Decl *decl, LLVMValueRef value) diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index fb16151ed..1935a9eb8 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -24,13 +24,22 @@ void gencontext_emit_compound_stmt(GenContext *context, Ast *ast) } } +void gencontext_emit_ct_compound_stmt(GenContext *context, Ast *ast) +{ + assert(ast->ast_kind == AST_CT_COMPOUND_STMT); + VECEACH(ast->compound_stmt.stmts, i) + { + gencontext_emit_stmt(context, ast->compound_stmt.stmts[i]); + } +} + static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast) { Decl *decl = ast->declare_stmt; LLVMTypeRef alloc_type = llvm_type(type_reduced(decl->type)); - decl->ref = gencontext_emit_alloca(context, alloc_type, decl->name); + decl->backend_ref = gencontext_emit_alloca(context, alloc_type, decl->name); if (decl->var.failable) { decl->var.failable_ref = gencontext_emit_alloca(context, llvm_type(type_error), decl->name); @@ -58,7 +67,7 @@ static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast) // If we don't have undef, then make an assign. if (init->expr_kind != EXPR_UNDEF) { - gencontext_emit_assign_expr(context, decl->ref, decl->var.init_expr, decl->var.failable_ref); + gencontext_emit_assign_expr(context, decl->backend_ref, decl->var.init_expr, decl->var.failable_ref); } // TODO trap on undef in debug mode. } @@ -67,7 +76,7 @@ static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast) // Normal case, zero init. gencontext_emit_store(context, decl, LLVMConstNull(alloc_type)); } - return decl->ref; + return decl->backend_ref; } void gencontext_emit_decl_expr_list_ignore_result(GenContext *context, Expr *expr) @@ -904,7 +913,7 @@ void gencontext_emit_catch_stmt(GenContext *context, Ast *ast) Decl *error_var = ast->catch_stmt.err_var; assert(error_var->type->canonical == type_error); error_result = gencontext_emit_alloca(context, llvm_type(type_error), error_var->name); - error_var->ref = error_result; + error_var->backend_ref = error_result; catch_expr = error_var->var.init_expr; } @@ -1009,6 +1018,9 @@ void gencontext_emit_stmt(GenContext *context, Ast *ast) case AST_COMPOUND_STMT: gencontext_emit_compound_stmt(context, ast); break; + case AST_CT_COMPOUND_STMT: + gencontext_emit_ct_compound_stmt(context, ast); + break; case AST_FOR_STMT: gencontext_emit_for_stmt(context, ast); break; diff --git a/src/compiler/number.c b/src/compiler/number.c index 64be8507c..3711bbd98 100644 --- a/src/compiler/number.c +++ b/src/compiler/number.c @@ -61,9 +61,6 @@ void expr_const_fprint(FILE *__restrict file, ExprConst *expr) case TYPE_FXX: fprintf(file, "%Lf", expr->f); break; - case TYPE_ENUM: - fprintf(file, "%s", expr->enum_constant->name); - break; case TYPE_STRING: fprintf(file, "%.*s", expr->string.len, expr->string.chars); break; @@ -246,9 +243,6 @@ const char *expr_const_to_error_string(const ExprConst *expr) case TYPE_FXX: asprintf(&buff, "%Lf", expr->f); return buff; - case TYPE_ENUM: - asprintf(&buff, "%s.%s", expr->enum_constant->type->name, expr->enum_constant->name); - return buff; case TYPE_STRING: asprintf(&buff, "\"%*.s\"", expr->string.len, expr->string.chars); return buff; diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 4fd52b2b9..15d60ee2d 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -104,9 +104,16 @@ bool parse_param_list(Context *context, Expr ***result, bool allow_type, TokenTy static Expr *parse_macro_ident(Context *context, Expr *left) { assert(!left && "Unexpected left hand side"); - Expr *macro_ident = EXPR_NEW_TOKEN(EXPR_IDENTIFIER, context->tok); - macro_ident->identifier_expr.is_macro = true; + Expr *macro_ident = EXPR_NEW_TOKEN(EXPR_MACRO_IDENTIFIER, context->tok); advance_and_verify(context, TOKEN_AT); + if (TOKEN_IS(TOKEN_CT_IDENT)) + { + macro_ident->ct_macro_ident_expr.identifier = TOKSTR(context->tok); + macro_ident->expr_kind = EXPR_MACRO_CT_IDENTIFIER; + advance_and_verify(context, TOKEN_CT_IDENT); + RANGE_EXTEND_PREV(macro_ident); + return macro_ident; + } bool had_error = false; macro_ident->identifier_expr.path = parse_path_prefix(context, &had_error); if (had_error) return poisoned_expr; @@ -424,13 +431,27 @@ static Expr *parse_access_expr(Context *context, Expr *left) static Expr *parse_identifier_with_path(Context *context, Path *path) { - Expr *expr = EXPR_NEW_TOKEN(EXPR_IDENTIFIER, context->tok); + Expr *expr = EXPR_NEW_TOKEN(context->tok.type == TOKEN_CONST_IDENT ? EXPR_CONST_IDENTIFIER : EXPR_IDENTIFIER , context->tok); expr->identifier_expr.identifier = TOKSTR(context->tok); expr->identifier_expr.path = path; advance(context); return expr; } +static Expr *parse_ct_ident(Context *context, Expr *left) +{ + assert(!left && "Unexpected left hand side"); + if (try_consume(context, TOKEN_CT_CONST_IDENT)) + { + SEMA_TOKID_ERROR(context->prev_tok, "Compile time identifiers may not be constants."); + return poisoned_expr; + } + Expr *expr = EXPR_NEW_TOKEN(EXPR_CT_IDENT, context->tok); + expr->ct_ident_expr.identifier = TOKSTR(context->tok); + advance(context); + return expr; +} + static Expr *parse_identifier(Context *context, Expr *left) { assert(!left && "Unexpected left hand side"); @@ -447,7 +468,6 @@ static Expr *parse_maybe_scope(Context *context, Expr *left) switch (context->tok.type) { case TOKEN_IDENT: - case TOKEN_CT_IDENT: case TOKEN_CONST_IDENT: return parse_identifier_with_path(context, path); case TOKEN_TYPE_IDENT: @@ -910,16 +930,11 @@ ParseRule rules[TOKEN_EOF + 1] = { [TOKEN_NULL] = { parse_null, NULL, PREC_NONE }, [TOKEN_INTEGER] = { parse_integer, NULL, PREC_NONE }, [TOKEN_CHAR_LITERAL] = { parse_char_lit, NULL, PREC_NONE }, - [TOKEN_IDENT] = { parse_maybe_scope, NULL, PREC_NONE }, - [TOKEN_TYPE_IDENT] = { parse_type_identifier, NULL, PREC_NONE }, - [TOKEN_CT_IDENT] = { parse_identifier, NULL, PREC_NONE }, [TOKEN_AT] = { parse_macro_ident, NULL, PREC_NONE }, - [TOKEN_CONST_IDENT] = { parse_identifier, NULL, PREC_NONE }, - [TOKEN_CT_CONST_IDENT] = { parse_identifier, NULL, PREC_NONE }, [TOKEN_STRING] = { parse_string_literal, NULL, PREC_NONE }, [TOKEN_REAL] = { parse_double, NULL, PREC_NONE }, [TOKEN_OR] = { NULL, parse_binary, PREC_LOGICAL }, - [TOKEN_AND] = { NULL, parse_binary, PREC_LOGICAL }, + [TOKEN_AND] = { parse_unary_expr, parse_binary, PREC_LOGICAL }, [TOKEN_EQ] = { NULL, parse_binary, PREC_ASSIGNMENT }, [TOKEN_PLUS_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, [TOKEN_PLUS_MOD_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, @@ -934,4 +949,13 @@ ParseRule rules[TOKEN_EOF + 1] = { [TOKEN_BIT_OR_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, [TOKEN_SHR_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, [TOKEN_SHL_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, + + [TOKEN_IDENT] = { parse_maybe_scope, NULL, PREC_NONE }, + [TOKEN_TYPE_IDENT] = { parse_type_identifier, NULL, PREC_NONE }, + [TOKEN_CT_IDENT] = { parse_ct_ident, NULL, PREC_NONE }, + [TOKEN_CONST_IDENT] = { parse_identifier, NULL, PREC_NONE }, + [TOKEN_CT_CONST_IDENT] = { parse_ct_ident, NULL, PREC_NONE }, + [TOKEN_CT_TYPE_IDENT] = { parse_type_identifier, NULL, PREC_NONE }, + //[TOKEN_HASH_TYPE_IDENT] = { parse_type_identifier(, NULL, PREC_NONE } + }; diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index fc8ddd908..7e0e7e6db 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -1,6 +1,7 @@ #include "compiler_internal.h" #include "parser_internal.h" +static Decl *parse_const_declaration(Context *context, Visibility visibility); static bool context_next_up_is_type_with_path(Context *context) { @@ -221,8 +222,8 @@ static inline Path *parse_module_path(Context *context) /** * * module_param - * : CT_IDENT - * | HASH_IDENT + * : TYPE_IDENT + * | IDENT * ; * * module_params @@ -249,14 +250,15 @@ static inline bool parse_optional_module_params(Context *context, TokenId **toke switch (context->tok.type) { case TOKEN_IDENT: - SEMA_TOKEN_ERROR(context->tok, "The module parameter must be a $ or #-prefixed name, did you forgot the '$'?"); + case TOKEN_TYPE_IDENT: return false; case TOKEN_COMMA: SEMA_TOKEN_ERROR(context->tok, "Unexpected ','"); return false; case TOKEN_CT_IDENT: - case TOKEN_TYPE_IDENT: - break; + case TOKEN_CT_TYPE_IDENT: + SEMA_TOKEN_ERROR(context->tok, "The module parameter cannot be a $-prefixed name."); + return false; default: SEMA_TOKEN_ERROR(context->tok, "Only generic parameters are allowed here as parameters to the module."); return false; @@ -384,7 +386,7 @@ bool consume_const_name(Context *context, const char* type) SEMA_TOKEN_ERROR(context->tok, "Names of %ss must be all upper case.", type); return false; } - if (!consume(context, TOKEN_CONST_IDENT, "'%s' should be followed by the name of the %s.", type, type)) return false; + if (!consume(context, TOKEN_CONST_IDENT, "The constant name was expected here, did you forget it?")) return false; return true; } @@ -683,7 +685,8 @@ Decl *parse_decl_after_type(Context *context, bool local, TypeInfo *type) return poisoned_decl; } - EXPECT_IDENT_FOR_OR("variable_name", poisoned_decl); + + EXPECT_IDENT_FOR_OR("variable name", poisoned_decl); TokenId name = context->tok.id; advance(context); @@ -703,6 +706,7 @@ Decl *parse_decl_after_type(Context *context, bool local, TypeInfo *type) return decl; } + /** * declaration ::= ('local' | 'const')? type variable ('=' expr)? * @@ -710,14 +714,22 @@ Decl *parse_decl_after_type(Context *context, bool local, TypeInfo *type) */ Decl *parse_decl(Context *context) { - bool local = TOKEN_IS(TOKEN_LOCAL); - bool constant = TOKEN_IS(TOKEN_CONST); - if (local || constant) advance(context); + bool local = try_consume(context, TOKEN_LOCAL); - TypeInfo *type_info = parse_type(context); - TypeInfo *type = TRY_TYPE_OR(type_info, poisoned_decl); + if (TOKEN_IS(TOKEN_CONST)) + { + if (local) + { + SEMA_TOKID_ERROR(context->prev_tok, "A 'local' variable cannot also be declared 'constant'."); + return poisoned_decl; + } + return parse_const_declaration(context, VISIBLE_LOCAL); + } + + TypeInfo *type = TRY_TYPE_OR(parse_type(context), poisoned_decl); bool failable = try_consume(context, TOKEN_BANG); + Decl *decl = TRY_DECL_OR(parse_decl_after_type(context, local, type), poisoned_decl); if (failable && decl->var.unwrap) { @@ -725,7 +737,6 @@ Decl *parse_decl(Context *context) return poisoned_decl; } decl->var.failable = failable; - if (constant) decl->var.kind = VARDECL_CONST; return decl; } @@ -734,40 +745,28 @@ Decl *parse_decl(Context *context) /** * const_decl - * : 'const' CT_CONST_IDENT '=' const_expr ';' - * | 'const' type IDENT '=' const_expr ';' + * : 'const' type? IDENT '=' const_expr * ; */ -static inline Decl *parse_const_declaration(Context *context, Visibility visibility) +static Decl *parse_const_declaration(Context *context, Visibility visibility) { advance_and_verify(context, TOKEN_CONST); Decl *decl = DECL_NEW_VAR(NULL, VARDECL_CONST, visibility); decl->span.loc = context->prev_tok; - // Parse the compile time constant. - if (try_consume(context, TOKEN_CT_CONST_IDENT)) - { - decl->var.kind = VARDECL_CONST_CT; - if (!is_all_upper(decl->name)) - { - SEMA_TOKEN_ERROR(context->tok, "Compile time constants must be all upper characters."); - return poisoned_decl; - } - } - else + if (parse_next_is_decl(context)) { decl->var.type_info = TRY_TYPE_OR(parse_type(context), poisoned_decl); - decl->name = TOKKSTR(context->tok); - decl->name_token = context->tok.id; - if (!consume_const_name(context, "constant")) return poisoned_decl; } + decl->name = TOKKSTR(context->tok); + decl->name_token = context->tok.id; + if (!consume_const_name(context, "const")) return poisoned_decl; CONSUME_OR(TOKEN_EQ, poisoned_decl); decl->var.init_expr = TRY_EXPR_OR(parse_initializer(context), poisoned_decl); - CONSUME_OR(TOKEN_EOS, poisoned_decl); return decl; } @@ -790,9 +789,14 @@ static inline Decl *parse_global_declaration(Context *context, Visibility visibi { SEMA_TOKEN_ERROR(context->tok, "'func' can't appear here, maybe you intended to put 'func' the type?"); advance(context); - return false; + return poisoned_decl; } + if (TOKEN_IS(TOKEN_CONST_IDENT)) + { + SEMA_TOKEN_ERROR(context->tok, "This looks like a constant variable, did you forget 'const'?"); + return poisoned_decl; + } if (!consume_ident(context, "global variable")) return poisoned_decl; if (try_consume(context, TOKEN_EQ)) @@ -890,7 +894,8 @@ bool parse_next_is_decl(Context *context) case TOKEN_CT_TYPE_IDENT: case TOKEN_ERR: case TOKEN_TYPEID: - return (next_tok == TOKEN_BANG) | (next_tok == TOKEN_STAR) | (next_tok == TOKEN_LBRACKET) | (next_tok == TOKEN_IDENT); + return (next_tok == TOKEN_BANG) | (next_tok == TOKEN_STAR) | (next_tok == TOKEN_LBRACKET) | (next_tok == TOKEN_IDENT) + | (next_tok == TOKEN_CONST_IDENT); case TOKEN_IDENT: if (next_tok != TOKEN_SCOPE) return false; return context_next_up_is_type_with_path(context); @@ -1197,6 +1202,13 @@ static inline Decl *parse_struct_declaration(Context *context, Visibility visibi return decl; } +static inline Decl *parse_top_level_const_declaration(Context *context, Visibility visibility) +{ + Decl *decl = TRY_DECL_OR(parse_const_declaration(context, visibility), poisoned_decl); + TRY_CONSUME_EOS_OR(poisoned_decl); + return decl; +} + /** * Parse statements up to the next '}', 'case' or 'default' */ @@ -1264,6 +1276,11 @@ static inline Decl *parse_generics_declaration(Context *context, Visibility visi static inline Decl *parse_define(Context *context, Visibility visibility) { + if (context->next_tok.type == TOKEN_CT_TYPE_IDENT || context->next_tok.type == TOKEN_CT_IDENT) + { + SEMA_TOKEN_ERROR(context->next_tok, "Compile time variables cannot be defined at the global level."); + return poisoned_decl; + } advance_and_verify(context, TOKEN_DEFINE); bool had_error = false; Path *path = parse_path_prefix(context, &had_error); @@ -1437,12 +1454,32 @@ static inline Decl *parse_macro_declaration(Context *context, Visibility visibil while (!try_consume(context, TOKEN_RPAREN)) { TypeInfo *parm_type = NULL; + VarDeclKind param_kind; TEST_TYPE: switch (context->tok.type) { case TOKEN_IDENT: - case TOKEN_CT_IDENT: + param_kind = VARDECL_PARAM; break; + case TOKEN_CT_IDENT: + param_kind = VARDECL_PARAM_CT; + break; + case TOKEN_AND: + advance(context); + if (!TOKEN_IS(TOKEN_IDENT)) + { + SEMA_TOKEN_ERROR(context->tok, "Only normal variables may be passed by reference."); + } + param_kind = VARDECL_PARAM_REF; + break; + case TOKEN_HASH_IDENT: + param_kind = VARDECL_PARAM_EXPR; + break; + case TOKEN_HASH_TYPE_IDENT: + param_kind = VARDECL_PARAM_EXPR; + break; + case TOKEN_ELLIPSIS: + TODO default: if (parm_type) { @@ -1452,7 +1489,7 @@ static inline Decl *parse_macro_declaration(Context *context, Visibility visibil parm_type = TRY_TYPE_OR(parse_type(context), poisoned_decl); goto TEST_TYPE; } - Decl *param = decl_new_var(context->tok.id, parm_type, VARDECL_PARAM, visibility); + Decl *param = decl_new_var(context->tok.id, parm_type, param_kind, visibility); advance(context); params = VECADD(params, param); COMMA_RPAREN_OR(poisoned_decl); @@ -1767,7 +1804,7 @@ void parse_imports(Context *context) * | attribute_declaration * ; * @param visibility - * @return true if parsing worked + * @return Decl* or a poison value if parsing failed */ Decl *parse_top_level_statement(Context *context) { @@ -1811,7 +1848,7 @@ Decl *parse_top_level_statement(Context *context) if (!check_no_visibility_before(context, visibility)) return poisoned_decl; return parse_ct_switch_top_level(context); case TOKEN_CONST: - return parse_const_declaration(context, visibility); + return parse_top_level_const_declaration(context, visibility); case TOKEN_STRUCT: case TOKEN_UNION: return parse_struct_declaration(context, visibility); @@ -1854,7 +1891,7 @@ Decl *parse_top_level_statement(Context *context) { return parse_global_declaration(context, visibility); } - SEMA_TOKEN_ERROR(context->tok, "Unexpected symbol found."); + SEMA_TOKEN_ERROR(context->tok, "Expected a top level declaration here."); return poisoned_decl; } } \ No newline at end of file diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index df51227e3..f509c2673 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -206,7 +206,7 @@ static inline Ast* parse_do_stmt(Context *context) static inline bool token_type_ends_case(TokenType type, TokenType case_type, TokenType default_type) { - return type == case_type || type == default_type || type == TOKEN_RBRACE; + return type == case_type || type == default_type || type == TOKEN_RBRACE || type == TOKEN_CT_ENDSWITCH; } static inline Ast *parse_case_stmts(Context *context, TokenType case_type, TokenType default_type) @@ -605,14 +605,13 @@ static inline Ast *parse_decl_or_expr_stmt(Context *context) static inline Ast *parse_define_stmt(Context *context) { Ast *ast = AST_NEW_TOKEN(AST_DEFINE_STMT, context->tok); - + TokenId start = context->tok.id; advance_and_verify(context, TOKEN_DEFINE); - Decl *decl = decl_new_var(context->tok.id, NULL, VARDECL_LOCAL_CT, VISIBLE_LOCAL); - ast->define_stmt = decl; - + Decl *decl; switch (context->tok.type) { case TOKEN_CT_IDENT: + decl = decl_new_var(context->tok.id, NULL, VARDECL_LOCAL_CT, VISIBLE_LOCAL); advance(context); if (try_consume(context, TOKEN_EQ)) { @@ -620,6 +619,7 @@ static inline Ast *parse_define_stmt(Context *context) } break; case TOKEN_CT_TYPE_IDENT: + decl = decl_new_var(context->tok.id, NULL, VARDECL_LOCAL_CT_TYPE, VISIBLE_LOCAL); advance(context); if (try_consume(context, TOKEN_EQ)) { @@ -630,35 +630,52 @@ static inline Ast *parse_define_stmt(Context *context) SEMA_TOKEN_ERROR(context->tok, "Expected a compile time variable name ('$Foo' or '$foo')."); return poisoned_ast; } + decl->span.loc = start; + ast->define_stmt = decl; + RANGE_EXTEND_PREV(decl); + RANGE_EXTEND_PREV(ast); TRY_CONSUME_EOS(); return ast; } +static inline Ast* parse_ct_compound_stmt(Context *context) +{ + Ast *stmts = AST_NEW_TOKEN(AST_CT_COMPOUND_STMT, context->tok); + while (1) + { + TokenType token = context->tok.type; + if (token == TOKEN_CT_ELSE || token == TOKEN_CT_ELIF || token == TOKEN_CT_ENDIF) break; + Ast *stmt = TRY_AST(parse_stmt(context)); + vec_add(stmts->ct_compound_stmt, stmt); + RANGE_EXTEND_PREV(stmts); + } + return stmts; +} + /** * ct_else_stmt - * : CT_ELSE compound_stmt + * : CT_ELSE ':' ct_compound_stmt */ static inline Ast* parse_ct_else_stmt(Context *context) { Ast *ast = AST_NEW_TOKEN(AST_CT_ELSE_STMT, context->tok); advance_and_verify(context, TOKEN_CT_ELSE); - ast->ct_elif_stmt.then = TRY_AST(parse_compound_stmt(context)); + TRY_CONSUME(TOKEN_COLON, "$else needs a ':', did you forget it?"); + ast->ct_else_stmt = TRY_AST(parse_ct_compound_stmt(context)); return ast; } /** * ct_elif_stmt - * : CT_ELIF '(' expression ')' compound_statement + * : CT_ELIF '(' expression ')' ':' ct_compound_stmt (ct_elif_stmt | ct_else_stmt)? */ static inline Ast *parse_ct_elif_stmt(Context *context) { Ast *ast = AST_NEW_TOKEN(AST_CT_ELIF_STMT, context->tok); advance_and_verify(context, TOKEN_CT_ELIF); - ast->ct_elif_stmt.expr = TRY_EXPR_OR(parse_const_paren_expr(context), poisoned_ast); - - ast->ct_elif_stmt.then = TRY_AST(parse_compound_stmt(context)); - + TRY_CONSUME(TOKEN_COLON, "$elif needs a ':' after the expression, did you forget it?"); + ast->ct_elif_stmt.then = TRY_AST(parse_ct_compound_stmt(context)); if (TOKEN_IS(TOKEN_CT_ELIF)) { ast->ct_elif_stmt.elif = TRY_AST(parse_ct_elif_stmt(context)); @@ -669,11 +686,10 @@ static inline Ast *parse_ct_elif_stmt(Context *context) } return ast; } + /** * ct_if_stmt - * : CT_IF '(' expression ')' compound_stmt - * | CT_IF '(' expression ')' compound_stmt elif_stmt - * | CT_IF '(' expression ')' compound_stmt else_stmt + * : CT_IF '(' expression ')' ':' ct_compound_stmt (ct_elif_stmt | ct_else_stmt) CT_ENDIF EOS * ; */ static inline Ast* parse_ct_if_stmt(Context *context) @@ -681,7 +697,8 @@ static inline Ast* parse_ct_if_stmt(Context *context) Ast *ast = AST_NEW_TOKEN(AST_CT_IF_STMT, context->tok); advance_and_verify(context, TOKEN_CT_IF); ast->ct_if_stmt.expr = TRY_EXPR_OR(parse_const_paren_expr(context), poisoned_ast); - ast->ct_if_stmt.then = TRY_AST(parse_compound_stmt(context)); + TRY_CONSUME(TOKEN_COLON, "$if needs a ':' after the expression, did you forget it?"); + ast->ct_if_stmt.then = TRY_AST(parse_ct_compound_stmt(context)); if (TOKEN_IS(TOKEN_CT_ELIF)) { ast->ct_if_stmt.elif = TRY_AST(parse_ct_elif_stmt(context)); @@ -690,6 +707,9 @@ static inline Ast* parse_ct_if_stmt(Context *context) { ast->ct_if_stmt.elif = TRY_AST(parse_ct_else_stmt(context)); } + advance_and_verify(context, TOKEN_CT_ENDIF); + RANGE_EXTEND_PREV(ast); + TRY_CONSUME_EOS(); return ast; } @@ -774,7 +794,7 @@ static inline Ast* parse_ct_for_stmt(Context *context) } /** - * CTSWITCH '(' expression ')' '{' ct_switch_body '}' + * CTSWITCH '(' expression ')' ':' '{' ct_switch_body '}' * * ct_switch_body * : ct_case_statement @@ -793,7 +813,29 @@ static inline Ast* parse_ct_switch_stmt(Context *context) Ast *ast = AST_NEW_TOKEN(AST_CT_SWITCH_STMT, context->tok); advance_and_verify(context, TOKEN_CT_SWITCH); ast->ct_switch_stmt.cond = TRY_EXPR_OR(parse_const_paren_expr(context), poisoned_ast); - if (!parse_switch_body(context, &ast->ct_switch_stmt.body, TOKEN_CT_CASE, TOKEN_CT_DEFAULT)) return poisoned_ast; + TRY_CONSUME(TOKEN_COLON, "Expected ':' after $switch expression, did you forget it?"); + Ast **cases = NULL; + while (!try_consume(context, TOKEN_CT_ENDSWITCH)) + { + Ast *result; + TokenType next = context->tok.type; + if (next == TOKEN_CT_CASE) + { + result = TRY_AST_OR(parse_case_stmt(context, TOKEN_CT_CASE, TOKEN_CT_DEFAULT), poisoned_ast); + } + else if (next == TOKEN_CT_DEFAULT) + { + result = TRY_AST_OR(parse_default_stmt(context, TOKEN_CT_CASE, TOKEN_CT_DEFAULT), poisoned_ast); + } + else + { + SEMA_TOKEN_ERROR(context->tok, "A '$case' or '$default' would be needed here, '%.*s' is not allowed.", TOKLEN(context->tok.id), TOKSTR(context->tok.id)); + return poisoned_ast; + } + vec_add(cases, result); + } + TRY_CONSUME_EOS(); + ast->ct_switch_stmt.body = cases; return ast; } @@ -872,6 +914,9 @@ Ast *parse_stmt(Context *context) case TOKEN_C_ULONGLONG: case TOKEN_TYPEID: case TOKEN_CT_TYPE_IDENT: + case TOKEN_HASH_TYPE_IDENT: + case TOKEN_HASH_CONST_IDENT: + case TOKEN_HASH_IDENT: case TOKEN_TYPE_IDENT: case TOKEN_ERR: case TOKEN_IDENT: @@ -1038,6 +1083,8 @@ Ast *parse_stmt(Context *context) case TOKEN_CT_ELIF: case TOKEN_CT_ELSE: case TOKEN_CT_DEFAULT: + case TOKEN_CT_ENDIF: + case TOKEN_CT_ENDSWITCH: case TOKEN_RPARBRA: case TOKEN_IN: case TOKEN_BANGBANG: diff --git a/src/compiler/parser_internal.h b/src/compiler/parser_internal.h index dede12829..fdec8b365 100644 --- a/src/compiler/parser_internal.h +++ b/src/compiler/parser_internal.h @@ -56,7 +56,7 @@ Expr *parse_type_access_expr_after_type(Context *context, TypeInfo *type_info); bool parse_next_is_decl(Context *context); bool parse_next_is_case_type(Context *context); bool parse_module(Context *context); - +Decl *parse_define_compile_time_variable(Context *context, bool global); bool try_consume(Context *context, TokenType type); bool consume(Context *context, TokenType type, const char *message, ...); bool consume_const_name(Context *context, const char* type); diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 97650465c..ee4e335e8 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -34,11 +34,6 @@ static bool sema_type_mismatch(Context *context, Expr *expr, Type *type, CastTyp SEMA_ERROR(expr, "A raw type cannot be used in an expression. Add the suffix '.typeid' to use it as a value."); return false; } - if (expr_type == type_member) - { - SEMA_ERROR(expr, "A raw member reference cannot be used in an expression."); - return false; - } const char *action = ""; switch (cast_type) { diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 9546149fc..0522396c1 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -560,43 +560,15 @@ static inline bool sema_analyse_macro(Context *context, Decl *decl) -static inline bool expr_is_constant_eval(Expr *expr) -{ - switch (expr->expr_kind) - { - case EXPR_CONST: - return true; - case EXPR_COMPOUND_LITERAL: - return expr_is_constant_eval(expr->expr_compound_literal.initializer); - case EXPR_INITIALIZER_LIST: - { - Expr** init_exprs = expr->expr_initializer.initializer_expr; - switch (expr->expr_initializer.init_type) - { - case INITIALIZER_NORMAL: - { - VECEACH(init_exprs, i) - { - if (!expr_is_constant_eval(init_exprs[i])) return false; - } - return true; - } - default: - return false; - } - } - default: - return false; - } -} static inline bool sema_analyse_global(Context *context, Decl *decl) { - if (decl->var.kind == VARDECL_CONST_CT) return true; - - if (!sema_resolve_type_info(context, decl->var.type_info)) return false; - decl->type = decl->var.type_info->type; - if (decl->var.init_expr) + if (decl->var.type_info) + { + if (!sema_resolve_type_info(context, decl->var.type_info)) return false; + decl->type = decl->var.type_info->type; + } + if (decl->var.init_expr && decl->type) { if (!sema_analyse_expr_of_required_type(context, decl->type, decl->var.init_expr, false)) return false; if (!expr_is_constant_eval(decl->var.init_expr)) @@ -605,7 +577,12 @@ static inline bool sema_analyse_global(Context *context, Decl *decl) SEMA_ERROR(decl->var.init_expr, "The expression must be a constant value."); return false; } + if (!decl->type) decl->type = decl->var.init_expr->type; } + // We expect a constant to actually be parsed correctly so that it has a value, so + // this should always be true. + assert(decl->type || decl->var.kind == VARDECL_CONST); + AttributeDomain domain = decl->var.kind == VARDECL_CONST ? ATTR_CONST : ATTR_FUNC; VECEACH(decl->attributes, i) { @@ -661,6 +638,13 @@ static inline bool sema_analyse_generic(Context *context, Decl *decl) return true; } +static inline bool sema_analyse_define(Context *context, Decl *decl) +{ + Path *path = decl->generic_decl.path; + TODO + return true; +} + static inline bool sema_analyse_error(Context *context __unused, Decl *decl) @@ -748,6 +732,9 @@ bool sema_analyse_decl(Context *context, Decl *decl) case DECL_GENERIC: if (!sema_analyse_generic(context, decl)) return decl_poison(decl); break; + case DECL_DEFINE: + if (!sema_analyse_define(context, decl)) return decl_poison(decl); + break; case DECL_ATTRIBUTE: TODO case DECL_POISONED: @@ -760,7 +747,6 @@ bool sema_analyse_decl(Context *context, Decl *decl) case DECL_CT_SWITCH: case DECL_CT_CASE: case DECL_CT_IF: - case DECL_DEFINE: UNREACHABLE } decl->resolve_status = RESOLVE_DONE; diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 69e7baa40..1ce40fc29 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -26,7 +26,8 @@ static TypeInfo *type_info_copy_from_macro(Context *context, TypeInfo *source); #define MACRO_COPY_AST(x) x = ast_copy_from_macro(context, x) bool sema_analyse_expr_may_be_function(Context *context, Expr *expr); - +static inline bool sema_expr_analyse_binary(Context *context, Type *to, Expr *expr); +static inline bool sema_analyse_expr_value(Context *context, Type *to, Expr *expr); static inline bool expr_const_int_valid(Expr *expr, Type *type) { if (expr_const_int_overflowed(&expr->const_expr)) @@ -96,6 +97,11 @@ static bool expr_is_ltype(Expr *expr) { switch (expr->expr_kind) { + case EXPR_CONST_IDENTIFIER: + case EXPR_MACRO_CT_IDENTIFIER: + return false; + case EXPR_CT_IDENT: + return true; case EXPR_IDENTIFIER: { Decl *decl = expr->identifier_expr.decl; @@ -103,10 +109,13 @@ static bool expr_is_ltype(Expr *expr) decl = decl_raw(decl); switch (decl->var.kind) { + case VARDECL_LOCAL_CT: + case VARDECL_LOCAL_CT_TYPE: case VARDECL_LOCAL: case VARDECL_GLOBAL: case VARDECL_PARAM: return true; + case VARDECL_CONST: default: return false; } @@ -125,6 +134,91 @@ static bool expr_is_ltype(Expr *expr) } } +static inline bool sema_cast_ident_rvalue(Context *context, Type *to, Expr *expr) +{ + Decl *decl = expr->identifier_expr.decl; + expr->identifier_expr.is_rvalue = true; + + switch (decl->decl_kind) + { + case DECL_FUNC: + SEMA_ERROR(expr, "Expected function followed by (...) or prefixed by &."); + return expr_poison(expr); + case DECL_MACRO: + SEMA_ERROR(expr, "Expected macro followed by (...) or prefixed by '&'."); + return expr_poison(expr); + case DECL_ENUM_CONSTANT: + expr_replace(expr, decl->enum_constant.expr); + return true; + case DECL_VAR: + break; + case DECL_TYPEDEF: + UNREACHABLE + case DECL_POISONED: + return expr_poison(expr); + case DECL_LABEL: + SEMA_ERROR(expr, "Did you intend to use the label '%s' here?", decl->name); + return expr_poison(expr); + case DECL_STRUCT: + SEMA_ERROR(expr, "Expected struct followed by (...) or '.'."); + return expr_poison(expr); + case DECL_UNION: + SEMA_ERROR(expr, "Expected union followed by (...) or '.'."); + return expr_poison(expr); + case DECL_ENUM: + SEMA_ERROR(expr, "Expected enum name followed by '.' and an enum value."); + return expr_poison(expr); + case DECL_ERR: + SEMA_ERROR(expr, "Did you forget a '!' after '%s'?", decl->name); + return expr_poison(expr); + case DECL_ARRAY_VALUE: + UNREACHABLE + case DECL_IMPORT: + case DECL_GENERIC: + case DECL_CT_IF: + case DECL_CT_ELSE: + case DECL_CT_ELIF: + case DECL_CT_SWITCH: + case DECL_CT_CASE: + case DECL_ATTRIBUTE: + UNREACHABLE + case DECL_DEFINE: + TODO + } + switch (decl->var.kind) + { + case VARDECL_CONST: + expr_replace(expr, expr_copy_from_macro(context, decl->var.init_expr)); + return sema_analyse_expr(context, to, expr); + case VARDECL_PARAM_REF: + TODO + case VARDECL_PARAM_EXPR: + expr_replace(expr, expr_copy_from_macro(context, decl->var.init_expr)); + return sema_analyse_expr(context, to, expr); + case VARDECL_PARAM_CT_TYPE: + TODO + case VARDECL_PARAM: + case VARDECL_GLOBAL: + case VARDECL_LOCAL: + break; + case VARDECL_MEMBER: + SEMA_ERROR(expr, "Expected '%s' followed by a method call or property.", decl->name); + return expr_poison(expr); + case VARDECL_PARAM_CT: + TODO + break; + case VARDECL_LOCAL_CT: + TODO + break; + case VARDECL_LOCAL_CT_TYPE: + TODO + break; + case VARDECL_ALIAS: + TODO + break; + } + return true; +} static ExprFailableStatus expr_is_failable(Expr *expr) { @@ -227,21 +321,29 @@ static inline Decl *decl_copy_label_from_macro(Context *context, Decl *to_copy, return to_copy; } -static inline bool sema_expr_analyse_enum_constant(Expr *expr, const char *name, Decl *decl) +static inline Decl *decl_find_enum_constant(const char *name, Decl *decl) { VECEACH(decl->enums.values, i) { Decl *enum_constant = decl->enums.values[i]; if (enum_constant->name == name) { - assert(enum_constant->resolve_status == RESOLVE_DONE); - expr->type = enum_constant->type; - expr->const_expr = enum_constant->enum_constant.expr->const_expr; - expr->expr_kind = EXPR_CONST; - return true; + return enum_constant; } } - return false; + return NULL; +} + +static inline bool sema_expr_analyse_enum_constant(Expr *expr, const char *name, Decl *decl) +{ + Decl *enum_constant = decl_find_enum_constant(name, decl); + if (!enum_constant) return false; + + assert(enum_constant->resolve_status == RESOLVE_DONE); + expr->type = decl->type; + expr->access_expr.ref = enum_constant; + expr->expr_kind = EXPR_MEMBER_ACCESS; + return true; } @@ -261,6 +363,128 @@ static inline bool find_possible_inferred_identifier(Type *to, Expr *expr) } } + +static inline bool sema_expr_analyse_identifier_resolve(Context *context, Type *to, Expr *expr, ExprIdentifier *id_expr) +{ + Decl *ambiguous_decl = NULL; + Decl *private_symbol = NULL; + expr->pure = true; + + DEBUG_LOG("Now resolving %s", id_expr->identifier); + Decl *decl = sema_resolve_symbol(context, + id_expr->identifier, + id_expr->path, + &ambiguous_decl, + &private_symbol); + if (!decl && !id_expr->path && to) + { + if (find_possible_inferred_identifier(to, expr)) return true; + } + + if (!decl) + { + if (private_symbol) + { + SEMA_ERROR(expr, "'%s' is not visible from this module.", id_expr->identifier); + } + else if (ambiguous_decl) + { + SEMA_ERROR(expr, "The name '%s' ambiguous, please add a path.", id_expr->identifier); + } + else + { + SEMA_ERROR(expr, "'%s' could not be found, did you spell it right?", id_expr->identifier); + } + return false; + } + + // Already handled + if (!decl_ok(decl)) return false; + + if (ambiguous_decl) + { + SEMA_ERROR(expr, + "Ambiguous symbol '%s' – both defined in %s and %s, please add the module name to resolve the ambiguity", + id_expr->identifier, + decl->module->name->module, + ambiguous_decl->module->name->module); + return false; + } + + if (decl->decl_kind == DECL_FUNC && !id_expr->path && decl->module != context->module) + { + SEMA_ERROR(expr, "Functions from other modules must be prefixed with the module name, please add '%s' in front.", decl->module->name->module); + return false; + } + if (decl->decl_kind == DECL_MACRO) + { + if (expr->expr_kind != EXPR_MACRO_IDENTIFIER) + { + SEMA_ERROR(expr, "Macro expansions must be prefixed with '@', try using '@%s(...)' instead.", decl->name); + return false; + } + id_expr->decl = decl; + expr->type = type_void; + return true; + } + if (expr->expr_kind == EXPR_MACRO_IDENTIFIER) + { + SEMA_ERROR(expr, "Only macro expansions can be prefixed with '@', please try to remove it.", decl->name); + return false; + } + if (decl->resolve_status != RESOLVE_DONE) + { + if (!sema_analyse_decl(context, decl)) return poisoned_decl; + } + if (decl->decl_kind == DECL_VAR && decl->var.failable) + { + expr->failable = true; + } + if (expr->expr_kind == EXPR_CONST_IDENTIFIER) + { + assert(decl->decl_kind == DECL_VAR); + assert(decl->var.kind == VARDECL_CONST); + assert(!decl->var.failable); + } + assert(decl->type); + expr->identifier_expr.decl = decl; + expr->type = decl->type; + expr->pure = true; + expr->constant = false; + DEBUG_LOG("Resolution successful of %s.", decl->name); + return true; +} + +bool expr_is_constant_eval(Expr *expr) +{ + switch (expr->expr_kind) + { + case EXPR_CONST: + return true; + case EXPR_COMPOUND_LITERAL: + return expr_is_constant_eval(expr->expr_compound_literal.initializer); + case EXPR_INITIALIZER_LIST: + { + Expr** init_exprs = expr->expr_initializer.initializer_expr; + switch (expr->expr_initializer.init_type) + { + case INITIALIZER_NORMAL: + { + VECEACH(init_exprs, i) + { + if (!expr_is_constant_eval(init_exprs[i])) return false; + } + return true; + } + default: + return false; + } + } + default: + return false; + } +} + static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr *expr) { Decl *ambiguous_decl = NULL; @@ -301,10 +525,10 @@ static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr if (ambiguous_decl) { SEMA_ERROR(expr, - "Ambiguous symbol '%s' – both defined in %s and %s, please add the module name to resolve the ambiguity", - expr->identifier_expr.identifier, - decl->module->name->module, - ambiguous_decl->module->name->module); + "Ambiguous symbol '%s' – both defined in %s and %s, please add the module name to resolve the ambiguity", + expr->identifier_expr.identifier, + decl->module->name->module, + ambiguous_decl->module->name->module); return false; } @@ -315,7 +539,7 @@ static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr } if (decl->decl_kind == DECL_MACRO) { - if (!expr->identifier_expr.is_macro) + if (expr->expr_kind != EXPR_MACRO_IDENTIFIER) { SEMA_ERROR(expr, "Macro expansions must be prefixed with '@', try using '@%s(...)' instead.", decl->name); return false; @@ -324,7 +548,7 @@ static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr expr->type = type_void; return true; } - if (expr->identifier_expr.is_macro) + if (expr->expr_kind == EXPR_MACRO_IDENTIFIER) { SEMA_ERROR(expr, "Only macro expansions can be prefixed with '@', please try to remove it.", decl->name); } @@ -341,22 +565,29 @@ static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr switch (decl->var.kind) { case VARDECL_CONST: - assert(decl->var.init_expr && decl->var.init_expr->resolve_status == RESOLVE_DONE); + if (!decl->type) + { + Expr *copy = expr_copy_from_macro(context, decl->var.init_expr); + if (!sema_analyse_expr(context, to, copy)) return false; + if (!expr_is_constant_eval(copy)) + { + SEMA_ERROR(expr, "Constant value did not evaluate to a constant."); + return false; + } + expr_replace(expr, copy); + return true; + } if (decl->var.failable) { SEMA_ERROR(expr, "Constants may never be 'failable', please remove the '!'."); return false; } - expr_replace(expr, expr_copy_from_macro(context, decl->var.init_expr)); - return true; - case VARDECL_CONST_CT: - expr_replace(expr, expr_copy_from_macro(context, decl->var.init_expr)); - return sema_analyse_expr(context, to, expr); + break; default: break; } } - assert(decl->type); + if (!decl->type) decl->type = type_void; expr->identifier_expr.decl = decl; expr->type = decl->type; expr->pure = true; @@ -365,6 +596,43 @@ static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr return true; } +static inline bool sema_expr_analyse_ct_identifier(Context *context, Type *to __unused, Expr *expr) +{ + Decl *ambiguous_decl = NULL; + Decl *private_symbol = NULL; + expr->pure = true; + + DEBUG_LOG("Now resolving %s", expr->ct_ident_expr.identifier); + Decl *decl = sema_resolve_symbol(context, + expr->ct_ident_expr.identifier, + NULL, + &ambiguous_decl, + &private_symbol); + + assert(!ambiguous_decl && !private_symbol); + if (!decl) + { + SEMA_ERROR(expr, "Compile time variable '%s' could not be found.", expr->ct_ident_expr.identifier); + return false; + } + + // Already handled + if (!decl_ok(decl)) + { + return expr_poison(expr); + } + + DEBUG_LOG("Resolution successful of %s.", decl->name); + assert(decl->decl_kind == DECL_VAR); + assert(decl->resolve_status == RESOLVE_DONE); + + expr->ct_ident_expr.decl = decl; + expr->type = decl->type; + expr->pure = true; + expr->constant = true; + return true; +} + static inline bool sema_expr_analyse_binary_sub_expr(Context *context, Type *to, Expr *left, Expr *right) { return sema_analyse_expr(context, to, left) & sema_analyse_expr(context, to, right); @@ -551,6 +819,48 @@ static inline bool sema_expr_analyse_func_call(Context *context, Type *to, Expr return sema_expr_analyse_func_invocation(context, &decl->func.function_signature, expr, decl, to, struct_var); } +static bool sema_check_stmt_compile_time(Context *context, Ast *ast); + +static bool sema_check_expr_compile_time(Context *context, Expr *expr) +{ + switch (expr->expr_kind) + { + case EXPR_CONST: + return true; + case EXPR_MACRO_BLOCK: + VECEACH(expr->macro_block.stmts, i) + { + if (!sema_check_stmt_compile_time(context, expr->macro_block.stmts[i])) return false; + } + return true; + default: + return false; + } + UNREACHABLE +} + +static bool sema_check_stmt_compile_time(Context *context, Ast *ast) +{ + switch (ast->ast_kind) + { + case AST_NOP_STMT: + return true; + case AST_RETURN_STMT: + if (!ast->return_stmt.expr) return true; + return expr_is_constant_eval(ast->return_stmt.expr); + case AST_EXPR_STMT: + return sema_check_expr_compile_time(context, ast->expr_stmt); + case AST_CT_COMPOUND_STMT: + case AST_COMPOUND_STMT: + VECEACH(ast->compound_stmt.stmts, i) + { + if (!sema_check_stmt_compile_time(context, ast->ct_compound_stmt[i])) return false; + } + return true; + default: + return false; + } +} static inline bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr *call_expr, Decl *decl) { // TODO failable @@ -564,26 +874,84 @@ static inline bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr Decl **func_params = decl->macro_decl.parameters; unsigned num_args = vec_size(args); + if (num_args != vec_size(func_params)) + { + // TODO + SEMA_ERROR(call_expr, "Mismatch on number of arguments."); + return false; + } + Decl **params = num_args > 0 ? VECNEW(Decl *, num_args) : NULL; for (unsigned i = 0; i < num_args; i++) { Expr *arg = args[i]; - Decl *param = func_params[i]; - if (!sema_analyse_expr(context, param->type, arg)) - { - return false; - } + Decl *param = decl_copy_local_from_macro(context, func_params[i]); + vec_add(params, param); + assert(param->decl_kind == DECL_VAR); + assert(param->resolve_status == RESOLVE_NOT_DONE); + param->resolve_status = RESOLVE_RUNNING; + // Maybe there's a type, but in general a macro param may be + // typeless. + // Maybe we should actually do something like: + // macro var foo(var a, var $b) to make it more uniform. if (param->var.type_info) { - if (!sema_resolve_type_info(context, param->var.type_info)) - { - return false; - } + // Resolve it + if (!sema_resolve_type_info(context, param->var.type_info)) return false; + // And set the type, we're done. param->type = param->var.type_info->type; } - else + switch (param->var.kind) { - param->type = arg->type; + case VARDECL_PARAM_REF: + // &foo + if (!sema_analyse_expr_value(context, param->type, arg)) return false; + if (param->type && param->type->canonical != arg->type->canonical) + { + SEMA_ERROR(arg, "'%s' cannot be implicitly cast to '%s'.", type_to_error_string(arg->type), type_to_error_string(param->type)); + return false; + } + break; + case VARDECL_PARAM: + // foo + if (!sema_analyse_expr_of_required_type(context, param->type, arg, false)) return false; + break; + case VARDECL_PARAM_EXPR: + // #foo + param->var.init_expr = arg; + break; + case VARDECL_PARAM_CT: + // $foo + if (!sema_analyse_expr_of_required_type(context, param->type, arg, false)) return false; + if (!expr_is_constant_eval(arg)) + { + SEMA_ERROR(arg, "A compile time parameter must always be a constant, did you mistake it for a normal paramter?"); + return false; + } + break; + case VARDECL_PARAM_CT_TYPE: + TODO + case VARDECL_CONST: + case VARDECL_GLOBAL: + case VARDECL_LOCAL: + case VARDECL_MEMBER: + case VARDECL_LOCAL_CT: + case VARDECL_LOCAL_CT_TYPE: + case VARDECL_ALIAS: + UNREACHABLE } + if (param->var.kind != VARDECL_PARAM_EXPR) + { + if (param->type) + { + if (!cast_implicit(context, arg, param->type)) return false; + } + else + { + param->type = arg->type; + } + } + param->var.init_expr = arg; + param->resolve_status = RESOLVE_DONE; } context->macro_nesting++; @@ -595,22 +963,15 @@ static inline bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr Ast *body = ast_copy_from_macro(context, decl->macro_decl.body); - call_expr->type = type_void; + TypeInfo *foo = decl->macro_decl.rtype; Ast **saved_returns = context_push_returns(context); - context->expected_block_type = to; + context->expected_block_type = foo ? foo->type : to; context_push_scope_with_flags(context, SCOPE_MACRO); for (unsigned i = 0; i < num_args; i++) { - Decl *param = func_params[i]; - if (args[i]->expr_kind == EXPR_CONST) - { - param = decl_copy_local_from_macro(context, param); - param->var.constant = true; - param->var.init_expr = args[i]; - args[i] = NULL; - } + Decl *param = params[i]; sema_add_local(context, param); } @@ -633,7 +994,7 @@ static inline bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr } } - Expr *first_return_expr = context->returns[0]->return_stmt.expr; + Expr *first_return_expr = vec_size(context->returns) ? context->returns[0]->return_stmt.expr : NULL; Type *left_canonical = first_return_expr ? first_return_expr->type->canonical : type_void; // Let's unify the return statements. left_canonical = unify_returns(context, left_canonical); @@ -643,6 +1004,18 @@ static inline bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr goto EXIT; } call_expr->type = left_canonical; + if (vec_size(context->returns) == 1) + { + Expr *result = context->returns[0]->return_stmt.expr; + if (result && expr_is_constant_eval(result)) + { + if (sema_check_stmt_compile_time(context, body)) + { + expr_replace(call_expr, result); + goto EXIT; + } + } + } call_expr->expr_kind = EXPR_MACRO_BLOCK; call_expr->macro_block.stmts = body->compound_stmt.stmts; call_expr->macro_block.params = func_params; @@ -661,7 +1034,7 @@ static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr expr->pure = false; Expr *func_expr = expr->call_expr.function; - if (!sema_analyse_expr_may_be_function(context, func_expr)) return false; + if (!sema_analyse_expr_value(context, NULL, func_expr)) return false; expr->failable = func_expr->failable; Decl *decl; Expr *struct_var = NULL; @@ -683,6 +1056,8 @@ static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr struct_var->type = type_get_ptr(struct_var->unary_expr.expr->type); } break; + case EXPR_MACRO_IDENTIFIER: + return sema_expr_analyse_macro_call(context, to, expr, func_expr->identifier_expr.decl); default: TODO } @@ -693,7 +1068,7 @@ static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr case DECL_FUNC: return sema_expr_analyse_func_call(context, to, expr, decl, struct_var); case DECL_MACRO: - return sema_expr_analyse_macro_call(context, to, expr, decl); + UNREACHABLE case DECL_GENERIC: return sema_expr_analyse_generic_call(context, to, expr); case DECL_POISONED: @@ -1183,8 +1558,9 @@ static inline bool sema_expr_analyse_type_access(Context *context, Expr *expr, T Decl *member = decl->strukt.members[i]; if (name == member->name) { + expr->expr_kind = EXPR_MEMBER_ACCESS; expr->access_expr.ref = member; - expr->type = type_member; + expr->type = member->type; return true; } } @@ -1226,18 +1602,17 @@ static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr) return true; } if (name == kw_alignof) - { - expr_rewrite_to_int_const(expr, type_usize, type_size(ref->type)); - return true; - } - if (name == kw_alignof) { expr_rewrite_to_int_const(expr, type_usize, type_abi_alignment(ref->type)); return true; } - if (name == kw_offsetof) + if (name == kw_ordinal) { - TODO + if (ref->decl_kind == DECL_ENUM_CONSTANT) + { + expr_rewrite_to_int_const(expr, type_usize, ref->enum_constant.ordinal); + return true; + } } if (name == kw_nameof) { @@ -1279,13 +1654,14 @@ static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr) Decl *member = ref->strukt.members[i]; if (name == member->name) { + expr->expr_kind = EXPR_MEMBER_ACCESS; expr->access_expr.ref = member; - expr->type = type_member; + expr->type = member->type; return true; } } - TODO SEMA_ERROR(expr, "No function or member '%s.%s' found.", "todo", name); + TODO return false; } @@ -1293,13 +1669,13 @@ static inline bool sema_expr_analyse_access(Context *context, Expr *expr) { Expr *parent = expr->access_expr.parent; bool was_group = parent->expr_kind == EXPR_GROUP; - if (!sema_analyse_expr(context, NULL, parent)) return false; + if (!sema_analyse_expr_value(context, NULL, parent)) return false; if (parent->type == type_typeinfo) { return sema_expr_analyse_type_access(context, expr, parent->type_expr, was_group); } - if (parent->type == type_member) + if (parent->expr_kind == EXPR_MEMBER_ACCESS) { return sema_expr_analyse_member_access(context, expr); } @@ -1369,6 +1745,7 @@ static inline bool sema_expr_analyse_access(Context *context, Expr *expr) } if (is_pointer) { + if (!sema_cast_ident_rvalue(context, NULL, expr->access_expr.parent)) return false; insert_access_deref(expr); } expr->constant = expr->access_expr.parent->constant; @@ -1802,6 +2179,68 @@ bool sema_expr_analyse_assign_right_side(Context *context, Expr *expr, Type *lef return true; } +static inline bool sema_expr_begin_analyse(Expr *expr) +{ + switch (expr->resolve_status) + { + case RESOLVE_NOT_DONE: + expr->resolve_status = RESOLVE_RUNNING; + return true; + case RESOLVE_RUNNING: + SEMA_ERROR(expr, "Recursive resolution of expression"); + return expr_poison(expr); + case RESOLVE_DONE: + return false; + } + UNREACHABLE +} + + +static inline bool sema_expr_analyse_ct_identifier_lvalue(Context *context, Expr *expr) +{ + if (!sema_expr_begin_analyse(expr)) return expr_ok(expr); + + Decl *ambiguous_decl = NULL; + Decl *private_symbol = NULL; + DEBUG_LOG("Now resolving %s", expr->ct_ident_expr.identifier); + Decl *decl = sema_resolve_symbol(context, + expr->ct_ident_expr.identifier, + NULL, + &ambiguous_decl, + &private_symbol); + assert(!ambiguous_decl && !private_symbol); + + // Skip if poisoned. + if (!decl_ok(decl)) return false; + + if (!decl) + { + SEMA_ERROR(expr, "The compile time variable '%s' was not defined in this scope.", expr->ct_ident_expr.identifier); + return expr_poison(expr); + } + + if ((intptr_t)decl->var.scope < (intptr_t)context->current_scope) + { + SEMA_ERROR(expr, "Cannot modify '%s' inside of a deeper scope.", decl->name); + return false; + } + expr->ct_ident_expr.decl = decl; + expr->resolve_status = RESOLVE_DONE; + return true; +} + + +static bool sema_expr_analyse_ct_identifier_assign(Context *context, Expr *expr, Expr *left, Expr *right) +{ + if (!sema_expr_analyse_ct_identifier_lvalue(context, left)) return false; + + // 3. Evaluate right side to required type. + if (!sema_expr_analyse_assign_right_side(context, expr, left->type, right, true)) return false; + + left->ct_ident_expr.decl->var.init_expr = right; + expr_replace(expr, right); + return true; +} /** @@ -1814,7 +2253,11 @@ static bool sema_expr_analyse_assign(Context *context, Expr *expr, Expr *left, E expr->pure = false; // 1. Evaluate left side - if (!sema_analyse_expr(context, NULL, left)) return false; + if (left->expr_kind == EXPR_CT_IDENT) + { + return sema_expr_analyse_ct_identifier_assign(context, expr, left, right); + } + if (!sema_analyse_expr_value(context, NULL, left)) return false; // 2. Check assignability if (!expr_is_ltype(left)) @@ -1836,6 +2279,40 @@ static bool sema_expr_analyse_assign(Context *context, Expr *expr, Expr *left, E } +/** + * Analyse *%= *= /= %= ^= |= &= + * + * @return true if analysis worked. + */ +static bool sema_expr_analyse_ct_common_assign(Context *context, Expr *expr, Expr *left) +{ + + // 1. Analyse left side. + if (!sema_expr_analyse_ct_identifier_lvalue(context, left)) return false; + + Decl *left_var = left->ct_ident_expr.decl; + + Expr *left_value = left_var->var.init_expr; + assert(left_value); + assert(!left_value->failable); + + expr->binary_expr.left = left_value; + + expr->binary_expr.operator = binaryop_assign_base_op(expr->binary_expr.operator); + + if (!sema_expr_analyse_binary(context, NULL, expr)) return false; + + if (expr->expr_kind != EXPR_CONST) + { + SEMA_ERROR(expr->binary_expr.right, "Expected a constant expression."); + return false; + } + + left->ct_ident_expr.decl->var.init_expr = expr; + + return true; +} + /** * Analyse *%= *= /= %= ^= |= &= * @@ -1846,8 +2323,12 @@ static bool sema_expr_analyse_common_assign(Context *context, Expr *expr, Expr * expr->pure = false; expr->constant = false; + if (left->expr_kind == EXPR_CT_IDENT) + { + return sema_expr_analyse_ct_common_assign(context, expr, left); + } // 1. Analyse left side. - if (!sema_analyse_expr(context, NULL, left)) return false; + if (!sema_analyse_expr_value(context, NULL, left)) return false; // 2. Verify that the left side is assignable. if (!expr_is_ltype(left)) @@ -1950,6 +2431,11 @@ static bool sema_expr_analyse_add_sub_assign(Context *context, Expr *expr, Expr expr->pure = false; expr->constant = false; + if (left->expr_kind == EXPR_CT_IDENT) + { + return sema_expr_analyse_ct_common_assign(context, expr, left); + } + bool is_mod = expr->binary_expr.operator == BINARYOP_ADD_MOD_ASSIGN || expr->binary_expr.operator == BINARYOP_SUB_MOD_ASSIGN; @@ -2816,25 +3302,118 @@ static bool sema_expr_analyse_deref(Context *context, Expr *expr, Expr *inner) return true; } +static inline bool sema_take_addr_of_var(Expr *expr, Decl *decl) +{ + if (decl->decl_kind != DECL_VAR) return false; + switch (decl->var.kind) + { + case VARDECL_LOCAL: + case VARDECL_GLOBAL: + case VARDECL_PARAM: + case VARDECL_PARAM_REF: + decl->has_addr = true; + return true; + case VARDECL_CONST: + if (!decl->var.type_info) + { + SEMA_ERROR(expr, "The constant is not typed, either type it or use && to take the reference to a temporary."); + SEMA_PREV(decl, "The constant was defined here."); + return false; + } + decl->has_addr = true; + return true; + case VARDECL_PARAM_EXPR: + SEMA_ERROR(expr, "It is not possible to take the address of a captured expression, but you can use && to take a reference to the temporary value."); + return false; + case VARDECL_PARAM_CT: + case VARDECL_PARAM_CT_TYPE: + case VARDECL_LOCAL_CT_TYPE: + case VARDECL_LOCAL_CT: + // May not be reached due to EXPR_CT_IDENT being handled elsewhere. + UNREACHABLE; + case VARDECL_MEMBER: + case VARDECL_ALIAS: + UNREACHABLE + } + UNREACHABLE +} + +static inline bool sema_take_addr_of_ident(Expr *inner) +{ + Decl *decl = inner->identifier_expr.decl; + decl = decl_raw(decl); + switch (decl->decl_kind) + { + case DECL_ENUM_CONSTANT: + case DECL_FUNC: + decl->has_addr = true; + return true; + case DECL_VAR: + return sema_take_addr_of_var(inner, decl); + default: + SEMA_ERROR(inner, "It is not possible to take the address of a '%s'.", type_to_error_string(inner->type)); + return false; + } +} + +static bool sema_take_addr_of(Expr *inner) +{ + switch (inner->expr_kind) + { + case EXPR_CT_IDENT: + case EXPR_MACRO_CT_IDENTIFIER: + SEMA_ERROR(inner, "It's not possible to take the address of a compile time value."); + return false; + case EXPR_MACRO_IDENTIFIER: + SEMA_ERROR(inner, "It's not possible to take the address of a macro."); + return false; + case EXPR_IDENTIFIER: + case EXPR_CONST_IDENTIFIER: + return sema_take_addr_of_ident(inner); + case EXPR_UNARY: + if (inner->unary_expr.operator == UNARYOP_DEREF) return true; + break; + case EXPR_ACCESS: + return sema_take_addr_of(inner->access_expr.parent); + case EXPR_GROUP: + return sema_take_addr_of(inner->group_expr); + case EXPR_SUBSCRIPT: + return sema_take_addr_of(inner->subscript_expr.expr); + case EXPR_SLICE: + return sema_take_addr_of(inner->slice_expr.expr); + default: + break; + } + SEMA_ERROR(inner, "To take the address of a temporary value, use '&&' instead of '&'.", type_to_error_string(inner->type)); + return false; +} + /** * Analyse &a * @return true if analysis succeeds. */ -static bool sema_expr_analyse_addr(Context *context, Expr *expr, Expr *inner) +static bool sema_expr_analyse_addr(Context *context, Type *to, Expr *expr, Expr *inner) { - if (inner->type->type_kind == TYPE_FUNC) + Type *pointee = to ? to->canonical->pointer : NULL; + + // 1. Evaluate the expression + REDO: + switch (inner->expr_kind) { - expr->type = type_get_ptr(inner->type); - return true; - } - // 1. Check that it is an lvalue. - if (!expr_is_ltype(inner)) - { - SEMA_ERROR(inner, "It is not possible to take the address of values, only of variables and memory locations.", type_to_error_string(inner->type)); - return false; + case EXPR_POISONED: + return false; + case EXPR_GROUP: + // We want to collapse any grouping here. + expr_replace(inner, inner->group_expr); + goto REDO; + default: + if (!sema_analyse_expr_value(context, NULL, inner)) return expr_poison(expr); } - // 2. Get the pointer of the underlying type. + // 2. Take the address. + if (!sema_take_addr_of(inner)) return expr_poison(expr); + + // 3. Get the pointer of the underlying type. expr->type = type_get_ptr(inner->type); expr->constant = inner->constant; expr->pure = inner->pure; @@ -3009,7 +3588,47 @@ static bool sema_expr_analyse_not(Context *context, Type *to, Expr *expr, Expr * UNREACHABLE } -static inline bool sema_expr_analyse_incdec(Context *context, Type *to, Expr *expr, Expr *inner) +static inline bool sema_expr_analyse_ct_incdec(Context *context, Expr *expr, Expr *inner) +{ + assert(inner->expr_kind == EXPR_CT_IDENT); + + if (!sema_expr_analyse_ct_identifier_lvalue(context, inner)) return false; + + Decl *var = inner->ct_ident_expr.decl; + Expr *start_value = var->var.init_expr; + assert(start_value->expr_kind == EXPR_CONST); + + switch (start_value->const_expr.kind) + { + case ALL_INTS: + break; + default: + SEMA_ERROR(expr, "The compile time variable '%s' does not hold an integer.", var->name); + return false; + } + + Expr *end_value = COPY(start_value); + + // Make the change. + BigInt change; + bigint_init_signed(&change, expr->unary_expr.operator == UNARYOP_DEC ? -1 : 1); + bigint_add(&end_value->const_expr.i, &start_value->const_expr.i, &change); + if (!expr_const_int_valid(end_value, start_value->type)) return false; + + var->var.init_expr = end_value; + + if (expr->expr_kind == EXPR_POST_UNARY) + { + expr_replace(expr, start_value); + } + else + { + expr_replace(expr, end_value); + } + return true; +} + +static inline bool sema_expr_analyse_incdec(Context *context, Expr *expr, Expr *inner) { expr->constant = false; expr->pure = false; @@ -3019,6 +3638,11 @@ static inline bool sema_expr_analyse_incdec(Context *context, Type *to, Expr *ex SEMA_ERROR(inner, "Expression cannot be assigned to."); return false; } + if (inner->expr_kind == EXPR_CT_IDENT) + { + return sema_expr_analyse_ct_incdec(context, expr, inner); + } + if (!type_is_numeric(inner->type->canonical) && inner->type->canonical->type_kind != TYPE_POINTER) { SEMA_ERROR(inner, "Expression must be a number or a pointer."); @@ -3028,6 +3652,21 @@ static inline bool sema_expr_analyse_incdec(Context *context, Type *to, Expr *ex return true; } +static inline bool sema_expr_analyse_taddr(Context *context, Type *to, Expr *expr, Expr *inner) +{ + Type *inferred_type = NULL; + if (to->type_kind == TYPE_POINTER) + { + inferred_type = to->pointer; + } + + if (!sema_analyse_expr(context, inferred_type, inner)) return false; + + expr->constant = inner->constant; + expr->pure = inner->pure; + expr->type = type_get_ptr(inner->type); + return true; +} static inline bool sema_expr_analyse_binary(Context *context, Type *to, Expr *expr) @@ -3105,8 +3744,7 @@ static inline bool sema_expr_analyse_unary(Context *context, Type *to, Expr *exp if (!sema_analyse_expr(context, NULL, inner)) return false; return sema_expr_analyse_deref(context, expr, inner); case UNARYOP_ADDR: - if (!sema_analyse_expr_may_be_function(context, inner)) return false; - return sema_expr_analyse_addr(context, expr, inner); + return sema_expr_analyse_addr(context, to, expr, inner); case UNARYOP_NEG: case UNARYOP_NEGMOD: if (!sema_analyse_expr(context, NULL, inner)) return false; @@ -3119,8 +3757,10 @@ static inline bool sema_expr_analyse_unary(Context *context, Type *to, Expr *exp return sema_expr_analyse_not(context, to, expr, inner); case UNARYOP_DEC: case UNARYOP_INC: - if (!sema_analyse_expr(context, NULL, inner)) return false; - return sema_expr_analyse_incdec(context, to, expr, inner); + if (!sema_analyse_expr_value(context, NULL, inner)) return false; + return sema_expr_analyse_incdec(context, expr, inner); + case UNARYOP_TADDR: + return sema_expr_analyse_taddr(context, to, expr, inner); case UNARYOP_ERROR: return false; } @@ -3132,9 +3772,9 @@ static inline bool sema_expr_analyse_post_unary(Context *context, Type *to, Expr assert(expr->resolve_status == RESOLVE_RUNNING); Expr *inner = expr->post_expr.expr; - if (!sema_analyse_expr(context, NULL, inner)) return false; + if (!sema_analyse_expr_value(context, NULL, inner)) return false; - return sema_expr_analyse_incdec(context, to, expr, inner); + return sema_expr_analyse_incdec(context, expr, inner); } @@ -3289,8 +3929,17 @@ static Expr *expr_copy_from_macro(Context *context, Expr *source_expr) Expr *expr = expr_shallow_copy(source_expr); switch (source_expr->expr_kind) { + case EXPR_ENUM_CONSTANT: + case EXPR_MEMBER_ACCESS: + UNREACHABLE case EXPR_UNDEF: return expr; + case EXPR_CONST_IDENTIFIER: + case EXPR_MACRO_IDENTIFIER: + case EXPR_CT_IDENT: + case EXPR_MACRO_CT_IDENTIFIER: + // TODO + return expr; case EXPR_TYPEINFO: MACRO_COPY_TYPE(expr->type_expr); return expr; @@ -3441,7 +4090,6 @@ static Ast *ast_copy_from_macro(Context *context, Ast *source) MACRO_COPY_EXPR(ast->ct_assert_stmt.message); return ast; case AST_BREAK_STMT: - TODO return ast; case AST_CASE_STMT: MACRO_COPY_AST(ast->case_stmt.body); @@ -3477,6 +4125,9 @@ static Ast *ast_copy_from_macro(Context *context, Ast *source) case AST_COMPOUND_STMT: MACRO_COPY_AST_LIST(ast->compound_stmt.stmts); return ast; + case AST_CT_COMPOUND_STMT: + MACRO_COPY_AST_LIST(ast->ct_compound_stmt); + return ast; case AST_CONTINUE_STMT: TODO return ast; @@ -3547,8 +4198,6 @@ static Ast *ast_copy_from_macro(Context *context, Ast *source) return ast; case AST_RETURN_STMT: MACRO_COPY_EXPR(ast->return_stmt.expr); - // TODO handle conversions? - TODO return ast; case AST_SWITCH_STMT: copy_flow(context, ast); @@ -3735,7 +4384,12 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr * { case EXPR_DECL_LIST: case EXPR_UNDEF: + case EXPR_ENUM_CONSTANT: + case EXPR_MEMBER_ACCESS: UNREACHABLE + case EXPR_MACRO_CT_IDENTIFIER: + case EXPR_CT_IDENT: + return sema_expr_analyse_ct_identifier(context, to, expr); case EXPR_FAILABLE: return sema_expr_analyse_failable(context, to, expr); case EXPR_POISONED: @@ -3789,7 +4443,9 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr * return true; case EXPR_TYPEID: return sema_expr_analyse_type(context, to, expr); + case EXPR_CONST_IDENTIFIER: case EXPR_IDENTIFIER: + case EXPR_MACRO_IDENTIFIER: return sema_expr_analyse_identifier(context, to, expr); case EXPR_CALL: return sema_expr_analyse_call(context, to, expr); @@ -3822,37 +4478,80 @@ bool sema_analyse_expr_of_required_type(Context *context, Type *to, Expr *expr, return cast_implicit(context, expr, to); } -bool sema_analyse_expr(Context *context, Type *to, Expr *expr) -{ +static inline bool sema_cast_ct_ident_rvalue(Context *context, Type *to, Expr *expr) +{ + Decl *decl = expr->ct_ident_expr.decl; + Expr *copy = MACRO_COPY_EXPR(decl->var.init_expr); + if (!sema_analyse_expr(context, to, copy)) return false; + expr_replace(expr, copy); + return true; +} + +static inline bool sema_cast_rvalue(Context *context, Type *to, Expr *expr) +{ + if (!expr_ok(expr)) return false; + switch (expr->expr_kind) + { + case EXPR_MEMBER_ACCESS: + if (expr->access_expr.ref->decl_kind == DECL_ENUM_CONSTANT) + { + Type *original_type = expr->type; + expr_replace(expr, expr->access_expr.ref->enum_constant.expr); + expr->type = original_type; + return true; + } + SEMA_ERROR(expr, "A member must be followed by '.' plus a property like 'sizeof'."); + return false; + case EXPR_TYPEINFO: + SEMA_ERROR(expr, "A type must be followed by either (...) or '.'."); + return false; + case EXPR_ENUM_CONSTANT: + assert(expr->expr_enum->enum_constant.expr->expr_kind == EXPR_CONST); + expr->const_expr = expr->expr_enum->enum_constant.expr->const_expr; + expr->expr_kind = EXPR_CONST; + break; + case EXPR_MACRO_CT_IDENTIFIER: + SEMA_ERROR(expr, "Expected compile time macro variable '%s' followed by (...).", expr->ct_macro_ident_expr.identifier); + return expr_poison(expr); + case EXPR_MACRO_IDENTIFIER: + SEMA_ERROR(expr, "Expected macro '%s' followed by (...).", expr->macro_identifier_expr.identifier); + return expr_poison(expr); + case EXPR_CT_IDENT: + if (!sema_cast_ct_ident_rvalue(context, to, expr)) return false; + break; + case EXPR_IDENTIFIER: + case EXPR_CONST_IDENTIFIER: + if (!sema_cast_ident_rvalue(context, to, expr)) return false; + break; + default: + break; + } + return to ? cast(context, expr, to, CAST_TYPE_OPTIONAL_IMPLICIT) : true; +} + +bool sema_analyse_expr_value(Context *context, Type *to, Expr *expr) +{ switch (expr->resolve_status) { case RESOLVE_NOT_DONE: expr->resolve_status = RESOLVE_RUNNING; if (!sema_analyse_expr_dispatch(context, to, expr)) return expr_poison(expr); expr->resolve_status = RESOLVE_DONE; - break; + return true; case RESOLVE_RUNNING: SEMA_ERROR(expr, "Recursive resolution of expression"); return expr_poison(expr); case RESOLVE_DONE: - if (!expr_ok(expr)) return false; - break; + return expr_ok(expr); + default: + UNREACHABLE } - if (expr->expr_kind == EXPR_IDENTIFIER) - { - if (expr->identifier_expr.decl->decl_kind == DECL_FUNC) - { - SEMA_ERROR(expr, "Expected function followed by (...) or prefixed by &."); - return false; - } - if (expr->identifier_expr.decl->decl_kind == DECL_MACRO) - { - SEMA_ERROR(expr, "Expected macro followed by (...) or prefixed by &."); - return false; - } - } - return to ? cast(context, expr, to, CAST_TYPE_OPTIONAL_IMPLICIT) : true; +} + +bool sema_analyse_expr(Context *context, Type *to, Expr *expr) +{ + return sema_analyse_expr_value(context, to, expr) && sema_cast_rvalue(context, to, expr); } bool sema_analyse_expr_may_be_function(Context *context, Expr *expr) diff --git a/src/compiler/sema_name_resolution.c b/src/compiler/sema_name_resolution.c index 2e6369e0b..896f1e4df 100644 --- a/src/compiler/sema_name_resolution.c +++ b/src/compiler/sema_name_resolution.c @@ -154,6 +154,7 @@ bool sema_add_member(Context *context, Decl *decl) { return sema_append_local(context, decl); } + bool sema_add_local(Context *context, Decl *decl) { Decl *dummy; diff --git a/src/compiler/sema_passes.c b/src/compiler/sema_passes.c index 420aa90d6..a3f9bc400 100644 --- a/src/compiler/sema_passes.c +++ b/src/compiler/sema_passes.c @@ -146,6 +146,10 @@ void sema_analysis_pass_decls(Context *context) { sema_analyse_decl(context, context->functions[i]); } + VECEACH(context->generic_defines, i) + { + sema_analyse_decl(context, context->generic_defines[i]); + } DEBUG_LOG("Pass finished with %d error(s).", diagnostics.errors); } diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 12c7d4260..43e7198e3 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -122,7 +122,7 @@ AstId context_start_defer(Context *context) static inline bool sema_analyse_block_return_stmt(Context *context, Ast *statement) { - assert(context->current_scope->flags & SCOPE_EXPR_BLOCK); + assert(context->current_scope->flags & (SCOPE_EXPR_BLOCK | SCOPE_MACRO)); context->current_scope->jump_end = true; if (statement->return_stmt.expr) { @@ -138,7 +138,7 @@ static inline bool sema_analyse_block_return_stmt(Context *context, Ast *stateme static inline bool sema_analyse_return_stmt(Context *context, Ast *statement) { // This might be a return in a function block or a macro which must be treated differently. - if (context->current_scope->flags & SCOPE_EXPR_BLOCK) + if (context->current_scope->flags & (SCOPE_EXPR_BLOCK | SCOPE_MACRO)) { return sema_analyse_block_return_stmt(context, statement); } @@ -331,6 +331,26 @@ static inline bool sema_analyse_declare_stmt(Context *context, Ast *statement) Decl *decl = statement->declare_stmt; assert(decl->decl_kind == DECL_VAR); if (!sema_add_local(context, decl)) return decl_poison(decl); + if (decl->var.kind == VARDECL_CONST) + { + Expr *init_expr = decl->var.init_expr; + if (!init_expr) + { + SEMA_ERROR(decl, "Constants need to have an initial value."); + return false; + } + if (init_expr->expr_kind == EXPR_TYPEINFO && init_expr->type_expr->resolve_status == RESOLVE_DONE + && init_expr->type_expr->type->type_kind == TYPE_VOID) + { + SEMA_ERROR(decl, "Constants cannot be undefined."); + return false; + } + if (!decl->var.type_info) + { + // Skip further evaluation. + return true; + } + } if (!sema_resolve_type_info(context, decl->var.type_info)) return decl_poison(decl); decl->type = decl->var.type_info->type; if (decl->var.init_expr) @@ -357,19 +377,61 @@ static inline bool sema_analyse_declare_stmt(Context *context, Ast *statement) static inline bool sema_analyse_define_stmt(Context *context, Ast *statement) { Decl *decl = statement->declare_stmt; + statement->ast_kind = AST_NOP_STMT; assert(decl->decl_kind == DECL_VAR); switch (decl->var.kind) { case VARDECL_LOCAL_CT_TYPE: if (decl->var.type_info && !sema_resolve_type_info(context, decl->var.type_info)) return false; break; - case VARDECL_LOCAL: - if (decl->var.init_expr) TODO; - TODO + case VARDECL_LOCAL_CT: + if (decl->var.type_info && !sema_resolve_type_info(context, decl->var.type_info)) return false; + if (decl->var.type_info) + { + decl->type = decl->var.type_info->type->canonical; + if (!type_is_builtin(decl->type->type_kind)) + { + SEMA_ERROR(decl->var.type_info, "Compile time variables may only be built-in types."); + return false; + } + if (decl->var.init_expr) + { + if (!sema_analyse_expr_of_required_type(context, decl->type, decl->var.init_expr, false)) return false; + if (decl->var.init_expr->expr_kind != EXPR_CONST) + { + SEMA_ERROR(decl->var.init_expr, "Expected a constant expression here."); + return false; + } + } + else + { + TODO // generate. + // decl->var.init_expr = + } + } + else + { + if (decl->var.init_expr) + { + if (!sema_analyse_expr(context, NULL, decl->var.init_expr)) return false; + if (decl->var.init_expr->expr_kind != EXPR_CONST) + { + SEMA_ERROR(decl->var.init_expr, "Expected a constant expression here."); + return false; + } + decl->type = decl->var.init_expr->type; + } + else + { + decl->type = type_void; + } + } + break; default: UNREACHABLE } - TODO; + decl->var.scope = context->current_scope; + return sema_add_local(context, decl); } static inline bool sema_analyse_expr_stmt(Context *context, Ast *statement) @@ -1398,6 +1460,20 @@ static bool sema_analyse_compound_stmt(Context *context, Ast *statement) return success; } +static bool sema_analyse_ct_compound_stmt(Context *context, Ast *statement) +{ + bool all_ok = ast_ok(statement); + VECEACH(statement->ct_compound_stmt, i) + { + if (!sema_analyse_statement(context, statement->ct_compound_stmt[i])) + { + ast_poison(statement->ct_compound_stmt[i]); + all_ok = false; + } + } + return all_ok; +} + static inline bool sema_analyse_statement_inner(Context *context, Ast *statement) { if (statement->ast_kind == AST_POISONED) @@ -1437,6 +1513,8 @@ static inline bool sema_analyse_statement_inner(Context *context, Ast *statement return sema_analyse_continue_stmt(context, statement); case AST_CT_ASSERT: return sema_analyse_ct_assert_stmt(context, statement); + case AST_CT_COMPOUND_STMT: + return sema_analyse_ct_compound_stmt(context, statement); case AST_CT_IF_STMT: return sema_analyse_ct_if_stmt(context, statement); case AST_DECLARE_STMT: diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index ff0600517..2a475a793 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -44,6 +44,7 @@ const char *kw_nameof; const char *kw_qnameof; const char *kw_kindof; const char *kw_len; +const char *kw_ordinal; void symtab_init(uint32_t capacity) { @@ -85,6 +86,7 @@ void symtab_init(uint32_t capacity) kw_qnameof = KW_DEF("qnameof"); kw_kindof = KW_DEF("kindof"); kw_len = KW_DEF("len"); + kw_ordinal = KW_DEF("ordinal"); attribute_list[ATTRIBUTE_INLINE] = KW_DEF("inline"); attribute_list[ATTRIBUTE_NOINLINE] = KW_DEF("noinline"); attribute_list[ATTRIBUTE_STDCALL] = KW_DEF("stdcall"); diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index 1c8145926..4445fab94 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -148,6 +148,12 @@ const char *token_type_to_string(TokenType type) return "CT_CONST_IDENT"; case TOKEN_CT_TYPE_IDENT: return "CT_TYPE_IDENT"; + case TOKEN_HASH_IDENT: + return "HASH_IDENT"; + case TOKEN_HASH_CONST_IDENT: + return "HASH_CONST_IDENT"; + case TOKEN_HASH_TYPE_IDENT: + return "HASH_TYPE_IDENT"; case TOKEN_CONST_IDENT: return "CONST_IDENT"; case TOKEN_TYPE_IDENT: @@ -338,6 +344,10 @@ const char *token_type_to_string(TokenType type) return "$else"; case TOKEN_CT_ELIF: return "$elif"; + case TOKEN_CT_ENDIF: + return "$endif"; + case TOKEN_CT_ENDSWITCH: + return "$endswitch"; case TOKEN_CT_IF: return "$if"; case TOKEN_CT_SWITCH: diff --git a/src/compiler/types.c b/src/compiler/types.c index 12bb9a8d4..668a08727 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -10,7 +10,7 @@ static Type t_f32, t_f64, t_fxx; static Type t_usz, t_isz; static Type t_cus, t_cui, t_cul, t_cull; static Type t_cs, t_ci, t_cl, t_cll; -static Type t_voidstar, t_typeid, t_error, t_typeinfo, t_member; +static Type t_voidstar, t_typeid, t_error, t_typeinfo; Type *type_bool = &t_u1; Type *type_void = &t_u0; @@ -20,7 +20,6 @@ Type *type_float = &t_f32; Type *type_double = &t_f64; Type *type_typeid = &t_typeid; Type *type_typeinfo = &t_typeinfo; -Type *type_member = &t_member; Type *type_char = &t_i8; Type *type_short = &t_i16; Type *type_int = &t_i32; @@ -488,7 +487,6 @@ type_create(#_name, &_shortname, _type, _bits, target->align_ ## _align, target- #undef DEF_TYPE type_create("typeinfo", &t_typeinfo, TYPE_TYPEINFO, 0, 0, 0); - type_create("member", &t_member, TYPE_MEMBER, 0, 0, 0); type_create("typeid", &t_typeid, TYPE_TYPEID, target->width_pointer, target->align_pref_pointer, target->align_pointer); type_create("void*", &t_voidstar, TYPE_POINTER, target->width_pointer, target->align_pref_pointer, target->align_pointer); create_type_cache(type_void); diff --git a/test/test_suite/constants/constants.c3t b/test/test_suite/constants/constants.c3t index 5535d2245..dc8a3d5e8 100644 --- a/test/test_suite/constants/constants.c3t +++ b/test/test_suite/constants/constants.c3t @@ -1,19 +1,20 @@ const byte AA = ~0; const byte BB = 200 ; const uint CC = ~0; -const uint DD = $FOO; +const uint DD = FOO; -const $FOO = ~0; +const FOO = ~0; uint x = AA; uint z = CC; -byte w = $FOO; -ushort v = $FOO; +byte w = FOO; +ushort v = FOO; uint z2 = DD; func void test() { - int xx = $FOO; + int xx = FOO; + int* yy = &&FOO; } // #expect: constants.ll @@ -30,5 +31,9 @@ func void test() entry: %xx = alloca i32 + %yy = alloca i32* + %taddr = alloca i32 store i32 -1, i32* %xx + store i32 -1, i32* %taddr + store i32* %taddr, i32** %yy ret void \ No newline at end of file diff --git a/test/test_suite/expressions/call_arg_types.c3 b/test/test_suite/expressions/call_arg_types.c3 index 9c21d6594..1960fc966 100644 --- a/test/test_suite/expressions/call_arg_types.c3 +++ b/test/test_suite/expressions/call_arg_types.c3 @@ -11,8 +11,8 @@ func void test1() test2(a); // #error: Cannot implicitly cast 'int' to 'char'. test2(100 + a); // #error: Cannot implicitly cast 'int' to 'char'. - const int x = 120; - test2(x); // #error: Cannot implicitly cast 'int' to 'char'. + const int X = 120; + test2(X); // #error: Cannot implicitly cast 'int' to 'char'. test2(100 + 100); // #error: Cannot fit '200' into type 'char'. } diff --git a/test/test_suite/globals/misplaced_const.c3 b/test/test_suite/globals/misplaced_const.c3 index 3701d68ce..d8ebafd84 100644 --- a/test/test_suite/globals/misplaced_const.c3 +++ b/test/test_suite/globals/misplaced_const.c3 @@ -1,6 +1,6 @@ -const $FOO = 3; -$BAR = 4; // #error: Did you forget a 'const' before the name of this compile time constant? +const FOO = 3; +int BAR = 4; // #error: This looks like a constant variable, did you forget 'const'? -const $BAZ = "ofke"; +const BAZ = "ofke"; -$FOOBAR; // #error: Compile time constant unexpectedly found \ No newline at end of file +FOOBAR; // #error: Expected a top level declaration here. \ No newline at end of file diff --git a/test/test_suite/symbols/various.c3 b/test/test_suite/symbols/various.c3 index 6cdc3f05e..8977c08c3 100644 --- a/test/test_suite/symbols/various.c3 +++ b/test/test_suite/symbols/various.c3 @@ -60,15 +60,17 @@ func void test8() func void test9() { - const char a = 1; // TODO should be "A" - char b = a; - a = b; // #error: Expression is not assignable + const char A = 1; + char b = A; + A = b; // #error: Expression is not assignable } func void test10() { - const char a = 1; - char* b = &a; // #error: address of values + const char B = 1; + char* c = &B; + const A = 1; + char* b = &A; // #error: To take the address of a temporary value, use '&&' instead of '&' } enum Enum : int @@ -181,4 +183,16 @@ int[2][3] b123; func void test24() { int a = b123; // #error: cast 'int[2][3]' to 'int' -} \ No newline at end of file +} + +func void test25() +{ + const A = void; // #error: Constants cannot be undefined. +} + +func void test26() +{ + const int A = void; // #error: Constants cannot be undefined. +} + +