diff --git a/resources/examples/acornvm/value.c3 b/resources/examples/acornvm/value.c3 index bd6c5b844..97f91b580 100644 --- a/resources/examples/acornvm/value.c3 +++ b/resources/examples/acornvm/value.c3 @@ -155,7 +155,7 @@ macro aTrue() /** * Is value null? - * @require value != nil + * @require value != null */ func bool Value.isNull(Value *value) @inline { @@ -164,7 +164,7 @@ func bool Value.isNull(Value *value) @inline /** * Is value false or null? - * @require value != nil + * @require value != null */ func bool Value.isFalse(Value *value) @inline { diff --git a/resources/testfragments/file1.c3 b/resources/testfragments/file1.c3 index 83047e838..756c62c1d 100644 --- a/resources/testfragments/file1.c3 +++ b/resources/testfragments/file1.c3 @@ -1,7 +1,63 @@ module baz; import bar; -public func void runBar() +struct Foo { - bar::notVisible(); // #error: 'notVisible' is not visible from this module. + 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); } \ No newline at end of file diff --git a/resources/testfragments/file2.c3 b/resources/testfragments/file2.c3 index 5a890db58..f4d602f18 100644 --- a/resources/testfragments/file2.c3 +++ b/resources/testfragments/file2.c3 @@ -1,6 +1,10 @@ module bar; -func void notVisible() -{ +struct Type { int x; } +public func Type blub(Type type) +{ +int i = 0; + type.x = i; + return type; } \ No newline at end of file diff --git a/resources/testfragments/sdl2.c3 b/resources/testfragments/sdl2.c3 index 2c51ffc5e..6730e8b70 100644 --- a/resources/testfragments/sdl2.c3 +++ b/resources/testfragments/sdl2.c3 @@ -20,10 +20,10 @@ const int SCREEN_HEIGHT = 480; //The window we'll be rendering to -void* gWindow = nil; +void* gWindow = null; //The surface contained by the window -void* gScreenSurface = nil; +void* gScreenSurface = null; extern func int init(uint flags) @cname("SDL_Init"); diff --git a/resources/testfragments/simple_test1.c3 b/resources/testfragments/simple_test1.c3 index 266ede4cf..14806d1ea 100644 --- a/resources/testfragments/simple_test1.c3 +++ b/resources/testfragments/simple_test1.c3 @@ -13,7 +13,7 @@ public func int Foo.test(Foo *foo) public func void gonk() { - Foo.test(nil); + Foo.test(null); printf("Bob\n"); } func void test() diff --git a/src/compiler/ast.c b/src/compiler/ast.c index aa9369615..23a45794b 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -97,6 +97,9 @@ Decl *decl_new_with_type(TokenId name, DeclKind decl_type, Visibility visibility case DECL_CT_ELIF: case DECL_ATTRIBUTE: case DECL_LABEL: + case DECL_CT_SWITCH: + case DECL_CT_CASE: + case DECL_DEFINE: UNREACHABLE } Type *type = type_new(kind, !name.index ? "anon" : TOKSTR(name)); @@ -311,6 +314,13 @@ void fprint_type_recursive(Context *context, FILE *file, Type *type, int indent) } switch (type->type_kind) { + + case TYPE_TYPEINFO: + DUMP("(type typeinfo)"); + return; + case TYPE_MEMBER: + DUMP("(type member)"); + return; case TYPE_POISONED: DUMP("(type poison)"); return; @@ -485,6 +495,11 @@ void fprint_expr_recursive(Context *context, FILE *file, Expr *expr, int indent) if (!expr) return; switch (expr->expr_kind) { + case EXPR_UNDEF: + DUMP("(undef)"); + return; + case EXPR_TYPEINFO: + TODO; case EXPR_SLICE_ASSIGN: DUMP("(sliceassign"); DUMPEXPC(expr); @@ -562,11 +577,6 @@ void fprint_expr_recursive(Context *context, FILE *file, Expr *expr, int indent) DUMPEXPC(expr); DUMPEXPR(expr->trycatch_expr); DUMPEND(); - case EXPR_TYPE_ACCESS: - DUMPF("(typeaccess .%s", TOKSTR(expr->type_access.name)); - DUMPEXPC(expr); - DUMPTI(expr->type_access.type); - DUMPEND(); case EXPR_ACCESS: DUMPF("(access .%s", TOKSTR(expr->access_expr.sub_element)); DUMPEXPC(expr); @@ -757,6 +767,9 @@ void fprint_decl_recursive(Context *context, FILE *file, Decl *decl, int indent) break; } DUMPEND(); + case DECL_DEFINE: + DUMPF("(define %s", decl->name); + DUMPEND(); case DECL_LABEL: DUMPF("(label %s", decl->name); DUMPEND(); @@ -864,6 +877,23 @@ void fprint_decl_recursive(Context *context, FILE *file, Decl *decl, int indent) DUMPF("(import %s", decl->name); // TODO DUMPEND(); + case DECL_CT_CASE: + if (decl->ct_case_decl.type) + { + DUMP("($case"); + DUMPTI(decl->ct_case_decl.type); + } + else + { + DUMP("($default"); + } + DUMPDECLS(decl->ct_case_decl.body); + DUMPEND(); + case DECL_CT_SWITCH: + DUMP("($switch"); + DUMPEXPR(decl->ct_switch_decl.expr); + DUMPDECLS(decl->ct_switch_decl.cases); + DUMPEND(); case DECL_ATTRIBUTE: DUMPF("(attribute %s)", decl->name); if (decl->attr.domains & ATTR_FUNC) diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 9354e690e..00564fffd 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -296,6 +296,17 @@ typedef struct Decl *elif; } CtIfDecl; +typedef struct +{ + Expr *expr; + Decl **cases; +} CtSwitchDecl; + +typedef struct +{ + TypeInfo *type; + Decl **body; +} CtCaseDecl; typedef struct { @@ -386,6 +397,13 @@ typedef struct Path *path; // For redefinition } GenericDecl; +typedef struct +{ + Path *path; + Expr **params; + Token alias; +} DefineDecl; + typedef struct { AstId defer; @@ -448,8 +466,11 @@ typedef struct _Decl TypedefDecl typedef_decl; MacroDecl macro_decl; GenericDecl generic_decl; + DefineDecl define_decl; CtIfDecl ct_if_decl; CtIfDecl ct_elif_decl; + CtSwitchDecl ct_switch_decl; + CtCaseDecl ct_case_decl; Decl** ct_else_decl; Expr *incr_array_decl; }; @@ -468,15 +489,6 @@ typedef struct }; } ExprElse; -typedef struct -{ - TypeInfo *type; - union - { - TokenId name; - Decl *decl; - }; -} ExprTypeAccess; typedef struct { @@ -660,10 +672,10 @@ struct _Expr ExprLen len_expr; ExprCast cast_expr; Expr *typeof_expr; + TypeInfo *type_expr; ExprConst const_expr; ExprRange range_expr; ExprStructValue struct_value_expr; - ExprTypeAccess type_access; ExprGuard guard_expr; Expr *trycatch_expr; ExprElse else_expr; @@ -1038,6 +1050,7 @@ typedef struct _Context Decl **global_decls; Decl **enums; Decl **types; + Decl **generic_defines; Decl **functions; Decl **methods; Decl **vars; @@ -1125,13 +1138,17 @@ 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; +extern Type *type_typeid, *type_error, *type_typeinfo, *type_member; extern const char *attribute_list[NUMBER_OF_ATTRIBUTES]; extern const char *kw_main; extern const char *kw_sizeof; +extern const char *kw_alignof; extern const char *kw_offsetof; +extern const char *kw_kindof; +extern const char *kw_nameof; +extern const char *kw_qnameof; extern const char *kw_len; #define AST_NEW_TOKEN(_kind, _token) new_ast(_kind, source_span_from_token_id(_token.id)) @@ -1289,7 +1306,7 @@ static inline void expr_replace(Expr *expr, Expr *replacement) void expr_const_set_int(ExprConst *expr, uint64_t v, TypeKind kind); void expr_const_set_float(ExprConst *expr, long double d, TypeKind kind); void expr_const_set_bool(ExprConst *expr, bool b); -void expr_const_set_nil(ExprConst *expr); +void expr_const_set_null(ExprConst *expr); void expr_const_fprint(FILE *__restrict file, ExprConst *expr); bool expr_const_int_overflowed(const ExprConst *expr); bool expr_const_compare(const ExprConst *left, const ExprConst *right, BinaryOp op); @@ -1420,6 +1437,7 @@ Type *type_find_common_ancestor(Type *left, Type *right); const char *type_to_error_string(Type *type); size_t type_size(Type *type); 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; } @@ -1431,6 +1449,19 @@ static inline bool type_is_unsigned(Type *type) { return type->type_kind >= TYPE static inline bool type_ok(Type *type) { return !type || type->type_kind != TYPE_POISONED; } static inline bool type_info_ok(TypeInfo *type_info) { return !type_info || type_info->kind != TYPE_INFO_POISON; } bool type_may_have_sub_elements(Type *type); +static inline bool type_kind_is_derived(TypeKind kind) +{ + switch (kind) + { + case TYPE_ARRAY: + case TYPE_POINTER: + case TYPE_VARARRAY: + case TYPE_SUBARRAY: + return true; + default: + return false; + } +} static inline Type *type_reduced(Type *type) { diff --git a/src/compiler/context.c b/src/compiler/context.c index e760c7713..01d5a8a4b 100644 --- a/src/compiler/context.c +++ b/src/compiler/context.c @@ -121,6 +121,10 @@ void context_register_global_decl(Context *context, Decl *decl) vec_add(context->types, decl); decl_set_external_name(decl); break; + case DECL_DEFINE: + vec_add(context->generic_defines, decl); + decl_set_external_name(decl); + break; case DECL_ENUM: vec_add(context->enums, decl); decl_set_external_name(decl); @@ -134,9 +138,11 @@ void context_register_global_decl(Context *context, Decl *decl) case DECL_CT_ELIF: case DECL_ATTRIBUTE: case DECL_LABEL: + case DECL_CT_CASE: UNREACHABLE break; case DECL_CT_IF: + case DECL_CT_SWITCH: vec_add(context->ct_ifs, decl); return; } @@ -204,6 +210,10 @@ void context_print_ast(Context *context, FILE *file) { fprint_decl(context, file, context->enums[i]); } + VECEACH(context->global_decls, i) + { + fprint_decl(context, file, context->global_decls[i]); + } VECEACH(context->vars, i) { fprint_decl(context, file, context->vars[i]); diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 4d0ba468d..fef57c0b6 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -149,9 +149,17 @@ typedef enum DECL_CT_IF, DECL_CT_ELSE, DECL_CT_ELIF, + DECL_CT_SWITCH, + DECL_CT_CASE, DECL_ATTRIBUTE, + DECL_DEFINE, } DeclKind; +#define NON_TYPE_DECLS DECL_ARRAY_VALUE: case DECL_IMPORT: case DECL_MACRO: \ + 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: case DECL_LABEL: \ + case DECL_DEFINE + // Ordering here is in priority if two branches should have the same exit. typedef enum { @@ -176,7 +184,6 @@ typedef enum EXPR_POST_UNARY, EXPR_TYPEID, EXPR_IDENTIFIER, - EXPR_TYPE_ACCESS, EXPR_CALL, EXPR_GROUP, EXPR_SUBSCRIPT, @@ -186,6 +193,7 @@ typedef enum EXPR_INITIALIZER_LIST, EXPR_EXPRESSION_LIST, EXPR_CAST, + EXPR_TYPEINFO, EXPR_TYPEOF, EXPR_SCOPED_EXPR, EXPR_EXPR_BLOCK, @@ -195,6 +203,7 @@ typedef enum EXPR_FAILABLE, EXPR_DECL_LIST, EXPR_LEN, + EXPR_UNDEF, } ExprKind; typedef enum @@ -472,6 +481,8 @@ typedef enum TYPE_ARRAY, TYPE_VARARRAY, TYPE_SUBARRAY, + TYPE_TYPEINFO, + TYPE_MEMBER, TYPE_TYPEID, TYPE_LAST = TYPE_TYPEID } TypeKind; diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 4a2838843..b97482291 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -243,6 +243,18 @@ void llvm_codegen_setup() void gencontext_emit_introspection_type(GenContext *context, Decl *decl) { llvm_type(decl->type); + if (decl_is_struct_type(decl)) + { + Decl **decls = decl->strukt.members; + VECEACH(decls, i) + { + Decl *member_decl = decls[i]; + if (decl_is_struct_type(member_decl)) + { + gencontext_emit_introspection_type(context, member_decl); + } + } + } LLVMValueRef global_name = LLVMAddGlobal(context->module, llvm_type(type_byte), decl->name ? decl->name : "anon"); LLVMSetGlobalConstant(global_name, 1); LLVMSetInitializer(global_name, LLVMConstInt(llvm_type(type_byte), 1, false)); @@ -301,15 +313,7 @@ static void gencontext_emit_decl(GenContext *context, Decl *decl) case DECL_ENUM: // TODO break; - case DECL_ARRAY_VALUE: - case DECL_IMPORT: - case DECL_MACRO: - case DECL_GENERIC: - case DECL_CT_IF: - case DECL_CT_ELSE: - case DECL_CT_ELIF: - case DECL_ATTRIBUTE: - case DECL_LABEL: + case NON_TYPE_DECLS: UNREACHABLE } } @@ -363,9 +367,11 @@ void llvm_codegen(Context *context) LLVMPassManagerBuilderUseInlinerWithThreshold(pass_manager_builder, 0); //get_inlining_threshold()); LLVMPassManagerRef pass_manager = LLVMCreatePassManager(); LLVMPassManagerRef function_pass_manager = LLVMCreateFunctionPassManagerForModule(gen_context.module); - LLVMAddAnalysisPasses(target_machine(), pass_manager); LLVMAddAnalysisPasses(target_machine(), function_pass_manager); + LLVMAddAnalysisPasses(target_machine(), pass_manager); LLVMPassManagerBuilderPopulateModulePassManager(pass_manager_builder, pass_manager); + // We insert a memcpy pass here, or it will be used too late to fix our aggregate copies. + LLVMAddMemCpyOptPass(function_pass_manager); LLVMPassManagerBuilderPopulateFunctionPassManager(pass_manager_builder, function_pass_manager); // IMPROVE diff --git a/src/compiler/llvm_codegen_debug_info.c b/src/compiler/llvm_codegen_debug_info.c index 8313cbdb3..996feb7ca 100644 --- a/src/compiler/llvm_codegen_debug_info.c +++ b/src/compiler/llvm_codegen_debug_info.c @@ -9,18 +9,10 @@ static inline LLVMMetadataRef gencontext_create_debug_type_from_decl(GenContext static LLVMMetadataRef debug_params[512]; switch (decl->decl_kind) { - case DECL_ATTRIBUTE: - case DECL_ENUM_CONSTANT: case DECL_POISONED: - case DECL_GENERIC: - case DECL_MACRO: - case DECL_CT_IF: - case DECL_CT_ELSE: - case DECL_CT_ELIF: case DECL_VAR: - case DECL_ARRAY_VALUE: - case DECL_IMPORT: - case DECL_LABEL: + case DECL_ENUM_CONSTANT: + case NON_TYPE_DECLS: UNREACHABLE; case DECL_FUNC: { @@ -143,6 +135,8 @@ LLVMMetadataRef gencontext_get_debug_type(GenContext *context, Type *type) case TYPE_IXX: case TYPE_FXX: case TYPE_TYPEID: + case TYPE_TYPEINFO: + case TYPE_MEMBER: UNREACHABLE case TYPE_BOOL: return gencontext_simple_debug_type(context, type, DW_ATE_boolean); diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index e01b9a848..6718a23ef 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -226,10 +226,14 @@ LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr) case EXPR_DESIGNATED_INITIALIZER: // Should only appear when generating designated initializers. UNREACHABLE + case EXPR_UNDEF: + // Should never occur here. + UNREACHABLE case EXPR_MACRO_BLOCK: TODO case EXPR_SLICE_ASSIGN: case EXPR_SLICE: + case EXPR_TYPEINFO: // Should never be an lvalue UNREACHABLE case EXPR_IDENTIFIER: @@ -254,7 +258,6 @@ LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr) case EXPR_BINARY: case EXPR_TERNARY: case EXPR_POST_UNARY: - case EXPR_TYPE_ACCESS: case EXPR_CALL: case EXPR_INITIALIZER_LIST: case EXPR_EXPRESSION_LIST: @@ -329,7 +332,7 @@ LLVMValueRef gencontext_emit_cast(GenContext *context, CastKind cast_kind, LLVMV case CAST_FPBOOL: return LLVMBuildFCmp(context->builder, LLVMRealUNE, value, LLVMConstNull(LLVMTypeOf(value)), "fpbool"); case CAST_BOOLFP: - return LLVMBuildSIToFP(context->builder, value, llvm_type(to_type), "boolfp"); + return LLVMBuildUIToFP(context->builder, value, llvm_type(to_type), "boolfp"); case CAST_INTBOOL: return LLVMBuildICmp(context->builder, LLVMIntNE, value, LLVMConstNull(LLVMTypeOf(value)), "intbool"); case CAST_FPFP: @@ -1830,21 +1833,10 @@ LLVMValueRef gencontext_emit_assign_expr(GenContext *context, LLVMValueRef ref, assign_block = gencontext_create_free_block(context, "after_assign"); context->error_var = failable_ref; context->catch_block = assign_block; + } + LLVMValueRef value = gencontext_emit_expr(context, expr); + LLVMBuildStore(context->builder, value, ref); - } - LLVMValueRef value; - switch (expr->expr_kind) - { - case EXPR_INITIALIZER_LIST: - value = gencontext_emit_load(context, - expr->type, - gencontext_emit_initializer_list_expr_addr(context, expr, ref)); - break; - default: - value = gencontext_emit_expr(context, expr); - LLVMBuildStore(context->builder, value, ref); - break; - } if (failable_ref) { LLVMBuildStore(context->builder, gencontext_emit_no_error_union(context), failable_ref); @@ -1912,6 +1904,10 @@ NESTED_RETRY: { case EXPR_POISONED: case EXPR_DECL_LIST: + case EXPR_TYPEINFO: + UNREACHABLE + case EXPR_UNDEF: + // Should never reach this. UNREACHABLE case EXPR_DESIGNATED_INITIALIZER: // Should only appear when generating designated initializers. @@ -1954,7 +1950,6 @@ NESTED_RETRY: return gencontext_emit_guard_expr(context, expr); case EXPR_TYPEID: return gencontext_emit_typeid(context, expr); - case EXPR_TYPE_ACCESS: case EXPR_TYPEOF: // These are folded in the semantic analysis step. UNREACHABLE diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index 28668d666..893df8ad7 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -334,15 +334,7 @@ void gencontext_emit_extern_decl(GenContext *context, Decl *decl) break; case DECL_ENUM: TODO - case DECL_ARRAY_VALUE: - case DECL_IMPORT: - case DECL_MACRO: - case DECL_GENERIC: - case DECL_CT_IF: - case DECL_CT_ELSE: - case DECL_CT_ELIF: - case DECL_ATTRIBUTE: - case DECL_LABEL: + case NON_TYPE_DECLS: UNREACHABLE } } diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index b73dcc009..fb16151ed 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -29,7 +29,8 @@ static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast) { Decl *decl = ast->declare_stmt; - decl->ref = gencontext_emit_alloca(context, llvm_type(type_reduced(decl->type)), decl->name); + LLVMTypeRef alloc_type = llvm_type(type_reduced(decl->type)); + decl->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); @@ -51,9 +52,20 @@ static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast) UsePointerValue); } */ - if (decl->var.init_expr) + Expr *init = decl->var.init_expr; + if (init) { - gencontext_emit_assign_expr(context, decl->ref, decl->var.init_expr, decl->var.failable_ref); + // 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); + } + // TODO trap on undef in debug mode. + } + else + { + // Normal case, zero init. + gencontext_emit_store(context, decl, LLVMConstNull(alloc_type)); } return decl->ref; } diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index 74f7b3a2b..6b0ba0e49 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -11,19 +11,11 @@ static inline LLVMTypeRef llvm_type_from_decl(LLVMContextRef context, Decl *decl static LLVMTypeRef params[MAX_PARAMS]; switch (decl->decl_kind) { - case DECL_ATTRIBUTE: + case DECL_VAR: case DECL_ENUM_CONSTANT: case DECL_POISONED: - case DECL_GENERIC: - case DECL_MACRO: - case DECL_CT_IF: - case DECL_CT_ELSE: - case DECL_CT_ELIF: - case DECL_VAR: - case DECL_ARRAY_VALUE: - case DECL_IMPORT: - case DECL_LABEL: - UNREACHABLE; + case NON_TYPE_DECLS: + UNREACHABLE case DECL_FUNC: { VECEACH(decl->func.function_signature.params, i) @@ -179,6 +171,8 @@ LLVMTypeRef llvm_get_type(LLVMContextRef context, Type *any_type) switch (any_type->type_kind) { case TYPE_POISONED: + case TYPE_TYPEINFO: + case TYPE_MEMBER: UNREACHABLE case TYPE_TYPEID: return any_type->backend_type = LLVMIntTypeInContext(context, any_type->builtin.bitsize); diff --git a/src/compiler/number.c b/src/compiler/number.c index ad66b6fd7..64be8507c 100644 --- a/src/compiler/number.c +++ b/src/compiler/number.c @@ -40,7 +40,7 @@ void expr_const_fprint(FILE *__restrict file, ExprConst *expr) switch (expr->kind) { case TYPE_POINTER: - fprintf(file, "nil"); + fprintf(file, "null"); break; case TYPE_BOOL: fprintf(file, expr->b ? "true" : "false"); @@ -92,7 +92,7 @@ void expr_const_set_bool(ExprConst *expr, bool b) expr->kind = TYPE_BOOL; } -void expr_const_set_nil(ExprConst *expr) +void expr_const_set_null(ExprConst *expr) { expr->i.digit_count = 0; expr->i.digit = 0; @@ -228,7 +228,7 @@ const char *expr_const_to_error_string(const ExprConst *expr) switch (expr->kind) { case TYPE_POINTER: - return "nil"; + return "null"; case TYPE_BOOL: return expr->b ? "true" : "false"; case TYPE_I8: diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 4d9961178..4fd52b2b9 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -145,6 +145,7 @@ static Expr *parse_type_identifier(Context *context, Expr *left) assert(!left && "Unexpected left hand side"); return parse_type_expression_with_path(context, NULL); } + static Expr *parse_cast_expr(Context *context, Expr *left) { assert(!left && "Unexpected left hand side"); @@ -408,7 +409,13 @@ static Expr *parse_access_expr(Context *context, Expr *left) Expr *access_expr = EXPR_NEW_EXPR(EXPR_ACCESS, left); access_expr->access_expr.parent = left; access_expr->access_expr.sub_element = context->tok.id; - TRY_CONSUME_OR(TOKEN_IDENT, "Expected identifier", poisoned_expr); + if (!try_consume(context, TOKEN_TYPEID)) + { + if (!try_consume(context, TOKEN_CONST_IDENT)) + { + TRY_CONSUME_OR(TOKEN_IDENT, "Expected identifier", poisoned_expr); + } + } access_expr->span = left->span; access_expr->span.end_loc = access_expr->access_expr.sub_element; return access_expr; @@ -787,25 +794,6 @@ Expr *parse_type_compound_literal_expr_after_type(Context *context, TypeInfo *ty return expr; } -Expr *parse_type_access_expr_after_type(Context *context, TypeInfo *type_info) -{ - switch (context->tok.type) - { - case TOKEN_TYPEID: - case TOKEN_IDENT: - case TOKEN_CONST_IDENT: - break; - default: - SEMA_TOKEN_ERROR(context->tok, "Expected the name of a type property here."); - return poisoned_expr; - } - Expr *expr = expr_new(EXPR_TYPE_ACCESS, type_info->span); - expr->type_access.type = type_info; - expr->type_access.name = context->tok.id; - advance(context); - RANGE_EXTEND_PREV(expr); - return parse_precedence_with_left_side(context, expr, PREC_CALL - 1); -} /** @@ -819,28 +807,27 @@ Expr *parse_type_access_expr_after_type(Context *context, TypeInfo *type_info) */ Expr *parse_type_expression_with_path(Context *context, Path *path) { - TypeInfo *type = type_info_new(TYPE_INFO_IDENTIFIER, path ? path->span : source_span_from_token_id(context->tok.id)); - type->unresolved.path = path; - type->unresolved.name_loc = context->tok.id; - advance_and_verify(context, TOKEN_TYPE_IDENT); - RANGE_EXTEND_PREV(type); + TypeInfo *type; + if (path) + { + type = type_info_new(TYPE_INFO_IDENTIFIER, path->span); + type->unresolved.path = path; + type->unresolved.name_loc = context->tok.id; + advance_and_verify(context, TOKEN_TYPE_IDENT); + RANGE_EXTEND_PREV(type); + type = TRY_TYPE_OR(parse_type_with_base(context, type), poisoned_expr); + } + else + { + type = TRY_TYPE_OR(parse_type(context), poisoned_expr); + } if (TOKEN_IS(TOKEN_LBRACE)) { return parse_type_compound_literal_expr_after_type(context, type); } - if (try_consume(context, TOKEN_BANG)) - { - Expr *expr = expr_new(EXPR_COMPOUND_LITERAL, type->span); - expr->expr_compound_literal.type_info = type; - expr->expr_compound_literal.initializer = expr_new(EXPR_INITIALIZER_LIST, type->span); - - Expr *failable = expr_new(EXPR_FAILABLE, expr->span); - failable->failable_expr = expr; - RANGE_EXTEND_PREV(failable); - return failable; - } - CONSUME_OR(TOKEN_DOT, poisoned_expr); - return parse_type_access_expr_after_type(context, type); + Expr *expr = expr_new(EXPR_TYPEINFO, type->span); + expr->type_expr = type; + return expr; } @@ -865,6 +852,24 @@ static Expr* parse_expr_block(Context *context, Expr *left) } ParseRule rules[TOKEN_EOF + 1] = { + [TOKEN_BOOL] = { parse_type_identifier, NULL, PREC_NONE }, + [TOKEN_BYTE] = { parse_type_identifier, NULL, PREC_NONE }, + [TOKEN_CHAR] = { parse_type_identifier, NULL, PREC_NONE }, + [TOKEN_SHORT] = { parse_type_identifier, NULL, PREC_NONE }, + [TOKEN_USHORT] = { parse_type_identifier, NULL, PREC_NONE }, + [TOKEN_INT] = { parse_type_identifier, NULL, PREC_NONE }, + [TOKEN_UINT] = { parse_type_identifier, NULL, PREC_NONE }, + [TOKEN_LONG] = { parse_type_identifier, NULL, PREC_NONE }, + [TOKEN_ULONG] = { parse_type_identifier, NULL, PREC_NONE }, + [TOKEN_ISIZE] = { parse_type_identifier, NULL, PREC_NONE }, + [TOKEN_USIZE] = { parse_type_identifier, NULL, PREC_NONE }, + [TOKEN_FLOAT] = { parse_type_identifier, NULL, PREC_NONE }, + [TOKEN_DOUBLE] = { parse_type_identifier, NULL, PREC_NONE }, + [TOKEN_HALF] = { parse_type_identifier, NULL, PREC_NONE }, + [TOKEN_QUAD] = { parse_type_identifier, NULL, PREC_NONE }, + [TOKEN_VOID] = { parse_type_identifier, NULL, PREC_NONE }, + [TOKEN_TYPEID] = { parse_type_identifier, NULL, PREC_NONE }, + [TOKEN_ELSE] = { NULL, parse_else_expr, PREC_TRY_ELSE }, [TOKEN_QUESTION] = { NULL, parse_ternary_expr, PREC_TERNARY }, [TOKEN_ELVIS] = { NULL, parse_ternary_expr, PREC_TERNARY }, diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index b7253ed0a..fc8ddd908 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -62,12 +62,12 @@ void recover_top_level(Context *context) #pragma mark --- Parse CT conditional code -static inline bool parse_conditional_top_level(Context *context, Decl ***decls) +static inline bool parse_top_level_block(Context *context, Decl ***decls) { CONSUME_OR(TOKEN_LBRACE, false); while (!TOKEN_IS(TOKEN_RBRACE) && !TOKEN_IS(TOKEN_EOF)) { - Decl *decl = parse_top_level(context); + Decl *decl = parse_top_level_statement(context); if (decl == NULL) continue; if (decl_ok(decl)) { @@ -82,21 +82,28 @@ static inline bool parse_conditional_top_level(Context *context, Decl ***decls) return true; } +/** + * ct_if_top_level ::= CT_IF const_paren_expr top_level_block + (CT_ELIF const_paren_expr top_level_block)* + (CT_ELSE top_level_block)? + * @param context + * @return the declaration if successfully parsed, poisoned_decl otherwise. + */ static inline Decl *parse_ct_if_top_level(Context *context) { Decl *ct = DECL_NEW(DECL_CT_IF, VISIBLE_LOCAL); advance_and_verify(context, TOKEN_CT_IF); - ct->ct_if_decl.expr = TRY_EXPR_OR(parse_paren_expr(context), poisoned_decl); + ct->ct_if_decl.expr = TRY_EXPR_OR(parse_const_paren_expr(context), poisoned_decl); - if (!parse_conditional_top_level(context, &ct->ct_if_decl.then)) return poisoned_decl; + if (!parse_top_level_block(context, &ct->ct_if_decl.then)) return poisoned_decl; CtIfDecl *ct_if_decl = &ct->ct_if_decl; while (TOKEN_IS(TOKEN_CT_ELIF)) { advance_and_verify(context, TOKEN_CT_ELIF); Decl *ct_elif = DECL_NEW(DECL_CT_ELIF, VISIBLE_LOCAL); - ct_elif->ct_elif_decl.expr = TRY_EXPR_OR(parse_paren_expr(context), poisoned_decl); - if (!parse_conditional_top_level(context, &ct_elif->ct_elif_decl.then)) return poisoned_decl; + ct_elif->ct_elif_decl.expr = TRY_EXPR_OR(parse_const_paren_expr(context), poisoned_decl); + if (!parse_top_level_block(context, &ct_elif->ct_elif_decl.then)) return poisoned_decl; ct_if_decl->elif = ct_elif; ct_if_decl = &ct_elif->ct_elif_decl; } @@ -105,7 +112,62 @@ static inline Decl *parse_ct_if_top_level(Context *context) advance_and_verify(context, TOKEN_CT_ELSE); Decl *ct_else = DECL_NEW(DECL_CT_ELSE, VISIBLE_LOCAL); ct_if_decl->elif = ct_else; - if (!parse_conditional_top_level(context, &ct_else->ct_else_decl)) return poisoned_decl; + if (!parse_top_level_block(context, &ct_else->ct_else_decl)) return poisoned_decl; + } + return ct; +} + +/** + * ct_case ::= (CT_DEFAULT | CT_CASE type) ':' top_level_statement* + * + * @param context + * @return poisoned decl if parsing fails. + */ +static inline Decl *parse_ct_case(Context *context) +{ + Decl *decl; + switch (context->tok.type) + { + case TOKEN_CT_DEFAULT: + advance(context); + decl = DECL_NEW(DECL_CT_CASE, VISIBLE_LOCAL); + break; + case TOKEN_CT_CASE: + decl = DECL_NEW(DECL_CT_CASE, VISIBLE_LOCAL); + advance(context); + decl->ct_case_decl.type = TRY_TYPE_OR(parse_type(context), poisoned_decl); + break; + default: + SEMA_TOKEN_ERROR(context->tok, "Expected a $case or $default statement here."); + return poisoned_decl; + } + TRY_CONSUME_OR(TOKEN_COLON, "Expected ':' here.", poisoned_decl); + while (1) + { + TokenType type = context->tok.type; + if (type == TOKEN_CT_DEFAULT || type == TOKEN_CT_CASE || type == TOKEN_LBRACE) break; + Decl *stmt = TRY_DECL_OR(parse_top_level_statement(context), poisoned_decl); + vec_add(decl->ct_case_decl.body, stmt); + } + return decl; +} + +/** + * ct_switch_top_level ::= CT_SWITCH const_paren_expr '{' ct_case* '}' + * @param context + * @return the declaration if successfully parsed, NULL otherwise. + */ +static inline Decl *parse_ct_switch_top_level(Context *context) +{ + Decl *ct = DECL_NEW(DECL_CT_SWITCH, VISIBLE_LOCAL); + advance_and_verify(context, TOKEN_CT_SWITCH); + ct->ct_switch_decl.expr = TRY_EXPR_OR(parse_const_paren_expr(context), poisoned_decl); + + CONSUME_OR(TOKEN_LBRACE, poisoned_decl); + while (!try_consume(context, TOKEN_RBRACE)) + { + Decl *result = TRY_DECL_OR(parse_ct_case(context), poisoned_decl); + vec_add(ct->ct_switch_decl.cases, result); } return ct; } @@ -113,9 +175,14 @@ static inline Decl *parse_ct_if_top_level(Context *context) #pragma mark --- Parse paths +/** + * module_path ::= IDENT (SCOPE IDENT)* + * + * @param context + * @return path or null if parsing failed. + */ static inline Path *parse_module_path(Context *context) { - assert(TOKEN_IS(TOKEN_IDENT)); char *scratch_ptr = context->path_scratch; size_t offset = 0; @@ -203,13 +270,10 @@ static inline bool parse_optional_module_params(Context *context, TokenId **toke } } /** - * module - * : MODULE path ';' - * | MODULE path '(' module_params ')' ';' + * module ::= MODULE module_path ('(' module_params ')')? EOS */ bool parse_module(Context *context) { - if (!try_consume(context, TOKEN_MODULE)) { return context_set_module_from_filename(context); @@ -249,23 +313,14 @@ bool parse_module(Context *context) } /** - * import_selective - * : import_spec - * | import_spec AS import_spec - * ; - * - * import_spec - * : IDENT - * | TYPE - * | MACRO - * | CONST_IDENT - * ; + * specified_import ::= IDENT (AS IDENT)? + * | CONST_IDENT (AS CONST_IDENT)? + * | TYPE_IDENT (AS TYPE_IDENT)? * * @return true if import succeeded */ -static inline bool parse_import_selective(Context *context, Path *path) +static inline bool parse_specified_import(Context *context, Path *path) { - // TODO constident, @ etc if (!token_is_symbol(context->tok.type)) { SEMA_TOKEN_ERROR(context->tok, "Expected a symbol name here, the syntax is 'import : '."); @@ -291,7 +346,6 @@ static inline bool parse_import_selective(Context *context, Path *path) Token alias = context->tok; advance(context); return context_add_import(context, path, symbol, alias); - } @@ -572,9 +626,8 @@ static inline TypeInfo *parse_array_type_index(Context *context, TypeInfo *type) * Assume already stepped into. * @return Type, poisoned if parsing is invalid. */ -TypeInfo *parse_type(Context *context) +TypeInfo *parse_type_with_base(Context *context, TypeInfo *type_info) { - TypeInfo *type_info = parse_base_type(context); while (type_info_ok(type_info)) { switch (context->tok.type) @@ -599,6 +652,20 @@ TypeInfo *parse_type(Context *context) return type_info; } +/** + * type + * : base_type + * | type '*' + * | type array_type_index + * + * Assume already stepped into. + * @return Type, poisoned if parsing is invalid. + */ +TypeInfo *parse_type(Context *context) +{ + return parse_type_with_base(context, parse_base_type(context)); +} + #pragma mark --- Decl parsing @@ -623,7 +690,6 @@ Decl *parse_decl_after_type(Context *context, bool local, TypeInfo *type) Visibility visibility = local ? VISIBLE_LOCAL : VISIBLE_MODULE; Decl *decl = decl_new_var(name, type, VARDECL_LOCAL, visibility); - if (TOKEN_IS(TOKEN_EQ)) { if (!decl) @@ -915,7 +981,7 @@ static inline bool parse_attributes(Context *context, Decl *parent_decl) if (TOKEN_IS(TOKEN_LPAREN)) { - attr->expr = TRY_EXPR_OR(parse_paren_expr(context), false); + attr->expr = TRY_EXPR_OR(parse_const_paren_expr(context), false); } const char *name = TOKSTR(attr->name); VECEACH(parent_decl->attributes, i) @@ -1199,36 +1265,38 @@ static inline Decl *parse_generics_declaration(Context *context, Visibility visi static inline Decl *parse_define(Context *context, Visibility visibility) { advance_and_verify(context, TOKEN_DEFINE); - TypeInfo *rtype = NULL; - if (!TOKEN_IS(TOKEN_IDENT)) - { - rtype = TRY_TYPE_OR(parse_type(context), poisoned_decl); - } - bool had_error; + bool had_error = false; Path *path = parse_path_prefix(context, &had_error); if (had_error) return poisoned_decl; - Decl *decl = decl_new(DECL_GENERIC, context->tok.id, visibility); - decl->generic_decl.path = path; - if (!consume_ident(context, "generic function name")) return poisoned_decl; - decl->generic_decl.rtype = rtype; - TokenId *parameters = NULL; + Decl *decl = decl_new(DECL_DEFINE, context->tok.id, visibility); + if (!token_is_symbol(context->tok.type)) + { + SEMA_TOKEN_ERROR(context->tok, "Expected an identifier here."); + return poisoned_decl; + } + advance(context); CONSUME_OR(TOKEN_LPAREN, poisoned_decl); + Expr **exprs = NULL; while (!try_consume(context, TOKEN_RPAREN)) { - if (!TOKEN_IS(TOKEN_IDENT)) + Expr *expr = TRY_EXPR_OR(parse_constant_expr(context), poisoned_decl); + vec_add(exprs, expr); + if (context->tok.type != TOKEN_RPAREN) { - SEMA_TOKEN_ERROR(context->tok, "Expected an identifier."); - return false; + TRY_CONSUME_OR(TOKEN_COMMA, "Expected ',' after argument.", poisoned_decl); } - parameters = VECADD(parameters, context->tok.id); - advance(context); - COMMA_RPAREN_OR(poisoned_decl); } - CONSUME_OR(TOKEN_LBRACE, poisoned_decl); - Ast **cases = NULL; - if (!parse_switch_body(context, &cases, TOKEN_CASE, TOKEN_DEFAULT)) return poisoned_decl; - decl->generic_decl.cases = cases; - decl->generic_decl.parameters = parameters; + decl->define_decl.path = path; + decl->define_decl.params = exprs; + TRY_CONSUME_OR(TOKEN_AS, "Expected 'as' after generic declaration.", poisoned_decl); + if (!token_is_symbol(context->tok.type)) + { + SEMA_TOKEN_ERROR(context->tok, "The aliased name must follow after 'as'."); + return poisoned_decl; + } + decl->define_decl.alias = context->tok; + advance(context); + TRY_CONSUME_EOS_OR(poisoned_decl); return decl; } @@ -1292,7 +1360,7 @@ static inline Decl *parse_attribute_declaration(Context *context, Visibility vis if (last_domain == 0) { SEMA_TOKEN_ERROR(context->tok, "Expected at least one domain for attribute '%s'.", decl->name); - return false; + return poisoned_decl; } if (!parse_opt_parameter_type_list(context, visibility, &decl->attr.attr_signature, false)) return poisoned_decl; TRY_CONSUME_EOS_OR(poisoned_decl); @@ -1636,16 +1704,9 @@ static inline bool check_no_visibility_before(Context *context, Visibility visib /** * - * import - * : IMPORT PATH ';' - * | IMPORT PATH ':' import_selective ';' - * | IMPORT IDENT AS IDENT LOCAL ';' - * | IMPORT IDENT LOCAL ';' + * import ::= IMPORT import_path (':' specified_import_list)? ';' * - * import_list - * : import_selective - * | import_list ',' import_selective - * ; + * specified_import_list ::= specified_import (',' specified_import)* * * @return true if import succeeded */ @@ -1664,7 +1725,7 @@ static inline bool parse_import(Context *context) { while (1) { - if (!parse_import_selective(context, path)) return false; + if (!parse_specified_import(context, path)) return false; if (!try_consume(context, TOKEN_COMMA)) break; } } @@ -1678,14 +1739,10 @@ static inline bool parse_import(Context *context) /** - * imports - * : import_decl - * | imports import_decl - * ; + * imports ::= import* */ void parse_imports(Context *context) { - while (TOKEN_IS(TOKEN_IMPORT)) { if (!parse_import(context)) recover_top_level(context); @@ -1694,6 +1751,8 @@ void parse_imports(Context *context) /** + * top_level_statement ::= visibility? top_level + * * top_level * : struct_declaration * | enum_declaration @@ -1710,9 +1769,8 @@ void parse_imports(Context *context) * @param visibility * @return true if parsing worked */ -Decl *parse_top_level(Context *context) +Decl *parse_top_level_statement(Context *context) { - Visibility visibility = VISIBLE_MODULE; switch (context->tok.type) { @@ -1744,11 +1802,14 @@ Decl *parse_top_level(Context *context) { Ast *ast = TRY_AST_OR(parse_ct_assert_stmt(context), false); vec_add(context->ct_asserts, ast); - return NULL; + return poisoned_decl; } case TOKEN_CT_IF: if (!check_no_visibility_before(context, visibility)) return poisoned_decl; return parse_ct_if_top_level(context); + case TOKEN_CT_SWITCH: + 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); case TOKEN_STRUCT: diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index 6f6775dfe..df51227e3 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -566,6 +566,34 @@ static inline Ast *parse_try_stmt(Context *context) return stmt; } +static inline Ast *parse_decl_or_expr_stmt(Context *context) +{ + Expr *expr = TRY_EXPR_OR(parse_expr(context), poisoned_ast); + Ast *ast = ast_calloc(); + ast->span = expr->span; + bool failable = false; + // We might be parsing "int!" + // If so we need to unwrap this. + if (expr->expr_kind == EXPR_FAILABLE && expr->failable_expr->expr_kind == EXPR_TYPEINFO) + { + failable = true; + expr_replace(expr, expr->failable_expr); + } + if (expr->expr_kind == EXPR_TYPEINFO) + { + ast->ast_kind = AST_DECLARE_STMT; + ast->declare_stmt = TRY_DECL_OR(parse_decl_after_type(context, true, expr->type_expr), poisoned_ast); + ast->declare_stmt->var.failable = failable; + } + else + { + ast->ast_kind = AST_EXPR_STMT; + ast->expr_stmt = expr; + } + CONSUME_OR(TOKEN_EOS, poisoned_ast); + return ast; +} + /** * define_stmt * : define CT_IDENT '=' const_expr EOS @@ -627,7 +655,7 @@ 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_paren_expr(context), poisoned_ast); + 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)); @@ -652,7 +680,7 @@ 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_paren_expr(context), poisoned_ast); + 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)); if (TOKEN_IS(TOKEN_CT_ELIF)) { @@ -764,7 +792,7 @@ 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_paren_expr(context), poisoned_ast); + 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; return ast; } @@ -786,15 +814,20 @@ static inline Ast *parse_assert_stmt(Context *context) #pragma mark --- External functions +/** + * ct_assert_stmt ::= CT_ASSERT '(' constant_expression (',' constant_expression) ')' ';' + * @param context + * @return + */ Ast *parse_ct_assert_stmt(Context *context) { Ast *ast = AST_NEW_TOKEN(AST_CT_ASSERT, context->tok); advance_and_verify(context, TOKEN_CT_ASSERT); TRY_CONSUME_OR(TOKEN_LPAREN, "'$assert' needs a '(' here, did you forget it?", poisoned_ast); - ast->ct_assert_stmt.expr = TRY_EXPR_OR(parse_expr(context), poisoned_ast); + ast->ct_assert_stmt.expr = TRY_EXPR_OR(parse_constant_expr(context), poisoned_ast); if (try_consume(context, TOKEN_COMMA)) { - ast->ct_assert_stmt.message = TRY_EXPR_OR(parse_expr(context), poisoned_ast); + ast->ct_assert_stmt.message = TRY_EXPR_OR(parse_constant_expr(context), poisoned_ast); } TRY_CONSUME_OR(TOKEN_RPAREN, "The ending ')' was expected here.", poisoned_ast); TRY_CONSUME_EOS(); @@ -842,14 +875,8 @@ Ast *parse_stmt(Context *context) case TOKEN_TYPE_IDENT: case TOKEN_ERR: case TOKEN_IDENT: - if (parse_next_is_decl(context)) - { - return parse_declaration_stmt(context); - } - else - { - return parse_expr_stmt(context); - } + case TOKEN_CONST_IDENT: + return parse_decl_or_expr_stmt(context); case TOKEN_TRY: return parse_try_stmt(context); case TOKEN_DEFINE: @@ -857,8 +884,6 @@ Ast *parse_stmt(Context *context) case TOKEN_LOCAL: // Local means declaration! case TOKEN_CONST: // Const means declaration! return parse_declaration_stmt(context); - case TOKEN_CONST_IDENT: - return parse_expr_stmt(context); case TOKEN_AT: return parse_expr_stmt(context); case TOKEN_RETURN: diff --git a/src/compiler/parser.c b/src/compiler/parser.c index 381aaed72..2e43a0695 100644 --- a/src/compiler/parser.c +++ b/src/compiler/parser.c @@ -107,7 +107,11 @@ bool consume(Context *context, TokenType type, const char *message, ...) #pragma mark --- Extern functions -static inline void parse_current(Context *context) +/** + * module? imports top_level_statement* + * @param context + */ +static inline void parse_translation_unit(Context *context) { // Prime everything advance(context); advance(context); @@ -115,8 +119,8 @@ static inline void parse_current(Context *context) parse_imports(context); while (!TOKEN_IS(TOKEN_EOF)) { - Decl *decl = parse_top_level(context); - if (!decl) continue; + Decl *decl = parse_top_level_statement(context); + assert(decl); if (decl_ok(decl)) { vec_add(context->global_decls, decl); @@ -132,7 +136,7 @@ void parse_file(Context *context) { lexer_init_with_file(&context->lexer, context->file); if (diagnostics.errors) return; - parse_current(context); + parse_translation_unit(context); } diff --git a/src/compiler/parser_internal.h b/src/compiler/parser_internal.h index 1ec4a1db2..dede12829 100644 --- a/src/compiler/parser_internal.h +++ b/src/compiler/parser_internal.h @@ -30,13 +30,14 @@ typedef enum DECL_PARSE_NORMAL, DECL_PARSE_UNWRAP } DeclParse; -Decl *parse_top_level(Context *context); +Decl *parse_top_level_statement(Context *context); Ast *parse_ct_assert_stmt(Context *context); Ast *parse_stmt(Context *context); Path *parse_path_prefix(Context *context, bool *had_error); Expr *parse_type_expression_with_path(Context *context, Path *path); Expr *parse_expr(Context *context); TypeInfo *parse_type(Context *context); +TypeInfo *parse_type_with_base(Context *context, TypeInfo *type_info); Expr* parse_constant_expr(Context *context); Expr *parse_initializer_list(Context *context); Expr *parse_initializer(Context *context); @@ -85,10 +86,10 @@ static inline bool expect_ident(Context *context, const char* name) } } -static inline Expr *parse_paren_expr(Context *context) +static inline Expr *parse_const_paren_expr(Context *context) { CONSUME_OR(TOKEN_LPAREN, poisoned_expr); - Expr *expr = TRY_EXPR_OR(parse_expr(context), poisoned_expr); + Expr *expr = TRY_EXPR_OR(parse_constant_expr(context), poisoned_expr); CONSUME_OR(TOKEN_RPAREN, poisoned_expr); return expr; } diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 0b97adca1..97650465c 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -28,6 +28,17 @@ static inline void insert_cast(Expr *expr, CastKind kind, Type *canonical) static bool sema_type_mismatch(Context *context, Expr *expr, Type *type, CastType cast_type) { + Type *expr_type = expr->type; + if (expr_type == type_typeinfo) + { + 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) { @@ -40,7 +51,6 @@ static bool sema_type_mismatch(Context *context, Expr *expr, Type *type, CastTyp case CAST_TYPE_OPTIONAL_IMPLICIT: UNREACHABLE } - Type *expr_type = expr->type; if (expr_type == expr_type->canonical) { if (type->canonical == type) @@ -491,7 +501,7 @@ bool xipt(Context *context, Expr *left, Type *from, Type *canonical, Type *type, SEMA_ERROR(left, "Cannot cast non zero constants into pointers."); return false; } - expr_const_set_nil(&left->const_expr); + expr_const_set_null(&left->const_expr); left->type = type; return true; } @@ -731,6 +741,8 @@ CastKind cast_to_bool_kind(Type *type) case TYPE_VARARRAY: case TYPE_SUBARRAY: case TYPE_TYPEID: + case TYPE_TYPEINFO: + case TYPE_MEMBER: // Improve consider vararray / subarray conversion to boolean. return CAST_ERROR; case TYPE_BOOL: @@ -766,6 +778,8 @@ bool cast(Context *context, Expr *expr, Type *to_type, CastType cast_type) case TYPE_POISONED: case TYPE_VOID: case TYPE_TYPEID: + case TYPE_TYPEINFO: + case TYPE_MEMBER: break; case TYPE_BOOL: // Bool may convert into integers and floats but only explicitly. diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 9ca608fca..9546149fc 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -757,9 +757,10 @@ bool sema_analyse_decl(Context *context, Decl *decl) case DECL_CT_ELSE: case DECL_CT_ELIF: case DECL_LABEL: - UNREACHABLE + case DECL_CT_SWITCH: + case DECL_CT_CASE: case DECL_CT_IF: - // Handled elsewhere + case DECL_DEFINE: UNREACHABLE } decl->resolve_status = RESOLVE_DONE; diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 047ccfe17..69e7baa40 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -14,7 +14,6 @@ static Expr **expr_copy_expr_list_from_macro(Context *context, Expr **expr_list) static Expr *expr_copy_from_macro(Context *context, Expr *source_expr); static Ast *ast_copy_from_macro(Context *context, Ast *source); static Ast **ast_copy_list_from_macro(Context *context, Ast **to_copy); -static bool sema_expr_analyse_type_access(Context *context, Type *to, Expr *expr); static Decl *decl_copy_local_from_macro(Context *context, Decl *to_copy); static TypeInfo *type_info_copy_from_macro(Context *context, TypeInfo *source); @@ -668,9 +667,6 @@ static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr Expr *struct_var = NULL; switch (func_expr->expr_kind) { - case EXPR_TYPE_ACCESS: - decl = func_expr->type_access.decl; - break; case EXPR_IDENTIFIER: decl = func_expr->identifier_expr.decl; break; @@ -953,87 +949,30 @@ static inline void expr_rewrite_to_int_const(Expr *expr_to_rewrite, Type *type, expr_to_rewrite->resolve_status = RESOLVE_DONE; } -static bool sema_expr_analyse_type_access(Context *context, Type *to, Expr *expr) +static inline void expr_rewrite_to_string(Expr *expr_to_rewrite, const char *string) +{ + expr_to_rewrite->expr_kind = EXPR_CONST; + expr_to_rewrite->constant = true; + expr_to_rewrite->const_expr.kind = TYPE_STRING; + expr_to_rewrite->const_expr.string.chars = (char *)string; + expr_to_rewrite->const_expr.string.len = (int)strlen(string); + expr_to_rewrite->pure = true; + expr_to_rewrite->resolve_status = RESOLVE_DONE; + expr_to_rewrite->type = type_string; +} + + +static bool sema_expr_analyse_typeinfo(Context *context, Expr *expr) { expr->constant = true; expr->pure = true; - TypeInfo *type_info = expr->type_access.type; + TypeInfo *type_info = expr->type_expr; if (!sema_resolve_type_info(context, type_info)) return false; - Type *canonical = type_info->type->canonical; - TokenType type = TOKTYPE(expr->type_access.name); - const char *name = TOKSTR(expr->type_access.name); - if (type == TOKEN_TYPEID) - { - expr->type = type_typeid; - expr->expr_kind = EXPR_TYPEID; - expr->typeid_expr = type_info; - expr->resolve_status = RESOLVE_DONE; - return true; - } - if (!type_may_have_sub_elements(canonical)) - { - SEMA_ERROR(expr, "'%s' does not have methods.", type_to_error_string(type_info->type)); - return false; - } - Decl *decl = canonical->decl; - // TODO add more constants that can be inspected? - // e.g. SomeEnum.values, MyUnion.x.offset etc? - switch (decl->decl_kind) - { - case DECL_ENUM: - if (type == TOKEN_CONST_IDENT) - { - if (!sema_expr_analyse_enum_constant(expr, name, decl)) - { - SEMA_ERROR(expr, "'%s' has no enumeration value '%s'.", decl->name, name); - return false; - } - return true; - } - if (name == kw_sizeof) - { - expr_rewrite_to_int_const(expr, type_usize, type_size(decl->enums.type_info->type)); - return true; - } - break; - case DECL_ERR: - case DECL_UNION: - case DECL_STRUCT: - if (name == kw_sizeof) - { - expr_rewrite_to_int_const(expr, type_usize, type_size(decl->type)); - return true; - } - break; - default: - UNREACHABLE - } - - - VECEACH(decl->methods, i) - { - Decl *function = decl->methods[i]; - if (name == function->name) - { - expr->type_access.decl = function; - expr->type = function->type; - return true; - } - } - VECEACH(decl->strukt.members, i) - { - Decl *member = decl->strukt.members[i]; - if (name == member->name) - { - expr->type_access.decl = member; - expr->type = member->type; - return true; - } - } - SEMA_ERROR(expr, "No function or member '%s.%s' found.", type_to_error_string(type_info->type), name); - return false; + expr->type = type_typeinfo; + return true; } + /* static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr) { @@ -1137,11 +1076,233 @@ static void add_members_to_context(Context *context, Decl *decl) } } +static inline bool sema_expr_analyse_type_access(Context *context, Expr *expr, TypeInfo *parent, bool was_group) +{ + if (!was_group && type_kind_is_derived(parent->type->type_kind)) + { + SEMA_ERROR(expr->access_expr.parent, "Array and pointer types must be enclosed in (), did you forget it?"); + return false; + } + + expr->constant = true; + expr->pure = true; + + Type *canonical = parent->type->canonical; + TokenType type = TOKTYPE(expr->access_expr.sub_element); + const char *name = TOKSTR(expr->access_expr.sub_element); + if (type == TOKEN_TYPEID) + { + expr->type = type_typeid; + expr->expr_kind = EXPR_TYPEID; + expr->typeid_expr = parent; + expr->resolve_status = RESOLVE_DONE; + return true; + } + if (name == kw_sizeof) + { + expr_rewrite_to_int_const(expr, type_usize, type_size(canonical)); + return true; + } + if (name == kw_alignof) + { + expr_rewrite_to_int_const(expr, type_usize, type_abi_alignment(canonical)); + return true; + } + if (name == kw_nameof) + { + expr_rewrite_to_string(expr, canonical->name); + return true; + } + if (name == kw_qnameof) + { + expr_rewrite_to_string(expr, type_generate_qname(canonical)); + return true; + } + if (!type_may_have_sub_elements(canonical)) + { + SEMA_ERROR(expr, "'%s' does not have a property '%s'.", type_to_error_string(parent->type), name); + return false; + } + Decl *decl = canonical->decl; + // TODO add more constants that can be inspected? + // e.g. SomeEnum.values, MyUnion.x.offset etc? + switch (decl->decl_kind) + { + case DECL_ENUM: + if (type == TOKEN_CONST_IDENT) + { + if (!sema_expr_analyse_enum_constant(expr, name, decl)) + { + SEMA_ERROR(expr, "'%s' has no enumeration value '%s'.", decl->name, name); + return false; + } + return true; + } + if (name == kw_sizeof) + { + expr_rewrite_to_int_const(expr, type_usize, type_size(decl->enums.type_info->type)); + return true; + } + if (name == kw_alignof) + { + expr_rewrite_to_int_const(expr, type_usize, type_abi_alignment(decl->enums.type_info->type)); + return true; + } + break; + case DECL_ERR: + case DECL_UNION: + case DECL_STRUCT: + if (name == kw_sizeof) + { + expr_rewrite_to_int_const(expr, type_usize, type_size(decl->type)); + return true; + } + if (name == kw_alignof) + { + expr_rewrite_to_int_const(expr, type_usize, type_abi_alignment(decl->type)); + return true; + } + break; + default: + UNREACHABLE + } + + + VECEACH(decl->methods, i) + { + Decl *function = decl->methods[i]; + if (name == function->name) + { + expr->access_expr.ref = function; + expr->type = function->type; + return true; + } + } + VECEACH(decl->strukt.members, i) + { + Decl *member = decl->strukt.members[i]; + if (name == member->name) + { + expr->access_expr.ref = member; + expr->type = type_member; + return true; + } + } + SEMA_ERROR(expr, "No function or member '%s.%s' found.", type_to_error_string(parent->type), name); + return false; +} + +static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr) +{ + Expr *parent = expr->access_expr.parent; + + expr->constant = true; + expr->pure = true; + + TokenType type = TOKTYPE(expr->access_expr.sub_element); + const char *name = TOKSTR(expr->access_expr.sub_element); + + Decl *ref = parent->access_expr.ref; + + bool is_plain_member = ref->decl_kind == DECL_VAR; + if (type == TOKEN_TYPEID) + { + expr->type = type_typeid; + expr->expr_kind = EXPR_TYPEID; + if (is_plain_member) + { + expr->typeid_expr = ref->var.type_info; + } + else + { + expr->typeid_expr = type_info_new_base(ref->type, parent->span); + } + expr->resolve_status = RESOLVE_DONE; + return true; + } + if (name == kw_sizeof) + { + 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_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) + { + TODO + } + if (name == kw_nameof) + { + TODO + } + if (name == kw_qnameof) + { + TODO + } + if (name == kw_kindof) + { + TODO + } + // If we have something like struct Foo { Bar b; int c; struct d { int e; } } + // If we are inspecting Foo.c we're done here. Otherwise handle struct d / Bar b case + // The same way. + if (is_plain_member) + { + if (!type_is_structlike(ref->type)) + { + SEMA_ERROR(expr, "'%s' does not have a member or property '%s'", type_to_error_string(ref->type), name); + return false; + } + // Pretend to be an inline struct. + ref = ref->type->decl; + } + VECEACH(ref->methods, i) + { + Decl *function = ref->methods[i]; + if (name == function->name) + { + expr->access_expr.ref = function; + expr->type = function->type; + return true; + } + } + VECEACH(ref->strukt.members, i) + { + Decl *member = ref->strukt.members[i]; + if (name == member->name) + { + expr->access_expr.ref = member; + expr->type = type_member; + return true; + } + } + TODO + SEMA_ERROR(expr, "No function or member '%s.%s' found.", "todo", name); + return false; +} + 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 (parent->type == type_typeinfo) + { + return sema_expr_analyse_type_access(context, expr, parent->type_expr, was_group); + } + if (parent->type == type_member) + { + return sema_expr_analyse_member_access(context, expr); + } expr->failable = parent->failable; assert(expr->expr_kind == EXPR_ACCESS); @@ -1601,7 +1762,7 @@ static inline bool sema_expr_analyse_cast(Context *context, Type *to, Expr *expr expr->constant = inner->constant; if (!success) return false; - if (!cast(context, inner, expr->cast_expr.type_info->type->canonical, CAST_TYPE_EXPLICIT)) return false; + if (!cast(context, inner, expr->cast_expr.type_info->type, CAST_TYPE_EXPLICIT)) return false; // TODO above is probably not right, cast type not set. // Overwrite cast. @@ -2567,6 +2728,8 @@ static bool sema_expr_analyse_comp(Context *context, Expr *expr, Expr *left, Exp case TYPE_POISONED: return false; case TYPE_VOID: + case TYPE_TYPEINFO: + case TYPE_MEMBER: case TYPE_TYPEDEF: UNREACHABLE case TYPE_BOOL: @@ -2635,10 +2798,10 @@ static bool sema_expr_analyse_deref(Context *context, Expr *expr, Expr *inner) SEMA_ERROR(inner, "Cannot dereference a value of type '%s'", type_to_error_string(inner->type)); return false; } - // 2. This could be a constant, in which case it is a nil which is an error. + // 2. This could be a constant, in which case it is a null which is an error. if (inner->expr_kind == EXPR_CONST) { - SEMA_ERROR(inner, "Dereferencing nil is not allowed."); + SEMA_ERROR(inner, "Dereferencing null is not allowed."); return false; } // 3. Now the type might not be a pointer because of a typedef, @@ -2838,6 +3001,8 @@ static bool sema_expr_analyse_not(Context *context, Type *to, Expr *expr, Expr * case TYPE_ENUM: case TYPE_ERRTYPE: case TYPE_TYPEID: + case TYPE_TYPEINFO: + case TYPE_MEMBER: SEMA_ERROR(expr, "Cannot use 'not' on %s", type_to_error_string(inner->type)); return false; } @@ -3124,6 +3289,11 @@ 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_UNDEF: + return expr; + case EXPR_TYPEINFO: + MACRO_COPY_TYPE(expr->type_expr); + return expr; case EXPR_SLICE_ASSIGN: MACRO_COPY_EXPR(expr->slice_assign_expr.left); MACRO_COPY_EXPR(expr->slice_assign_expr.right); @@ -3200,9 +3370,6 @@ static Expr *expr_copy_from_macro(Context *context, Expr *source_expr) case EXPR_IDENTIFIER: // TODO return expr; - case EXPR_TYPE_ACCESS: - MACRO_COPY_TYPE(expr->type_access.type); - return expr; case EXPR_CALL: MACRO_COPY_EXPR(expr->call_expr.function); MACRO_COPY_EXPR_LIST(expr->call_expr.arguments); @@ -3523,6 +3690,19 @@ static inline bool sema_expr_analyse_failable(Context *context, Type *to, Expr * if (!sema_analyse_expr(context, NULL, inner)) return false; expr->pure = inner->pure; expr->constant = inner->constant; + if (inner->expr_kind == EXPR_TYPEINFO) + { + TypeInfo *inner_type_info = inner->type_expr; + if (inner_type_info->type->type_kind != TYPE_ERRTYPE) + { + SEMA_ERROR(inner, "This must be an error type."); + return false; + } + inner->expr_kind = EXPR_COMPOUND_LITERAL; + inner->expr_compound_literal.type_info = inner_type_info; + inner->expr_compound_literal.initializer = NULL; + inner->type = inner_type_info->type; + } if (inner->failable) { SEMA_ERROR(inner, "The inner expression is already a failable."); @@ -3554,6 +3734,7 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr * switch (expr->expr_kind) { case EXPR_DECL_LIST: + case EXPR_UNDEF: UNREACHABLE case EXPR_FAILABLE: return sema_expr_analyse_failable(context, to, expr); @@ -3567,6 +3748,8 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr * case EXPR_MACRO_BLOCK: case EXPR_SCOPED_EXPR: UNREACHABLE + case EXPR_TYPEINFO: + return sema_expr_analyse_typeinfo(context, expr); case EXPR_SLICE: return sema_expr_analyse_slice(context, expr); case EXPR_CATCH: @@ -3608,8 +3791,6 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr * return sema_expr_analyse_type(context, to, expr); case EXPR_IDENTIFIER: return sema_expr_analyse_identifier(context, to, expr); - case EXPR_TYPE_ACCESS: - return sema_expr_analyse_type_access(context, to, expr); case EXPR_CALL: return sema_expr_analyse_call(context, to, expr); case EXPR_SUBSCRIPT: diff --git a/src/compiler/sema_passes.c b/src/compiler/sema_passes.c index 3431af8d7..420aa90d6 100644 --- a/src/compiler/sema_passes.c +++ b/src/compiler/sema_passes.c @@ -97,6 +97,7 @@ void sema_analysis_pass_conditional_compilation(Context *context) DEBUG_LOG("Pass: Top level conditionals %s", context->file->name); for (unsigned i = 0; i < vec_size(context->ct_ifs); i++) { + // Also handle switch! sema_analyse_top_level_if(context, context->ct_ifs[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 376d09774..12c7d4260 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -335,8 +335,17 @@ static inline bool sema_analyse_declare_stmt(Context *context, Ast *statement) decl->type = decl->var.type_info->type; if (decl->var.init_expr) { - if (!sema_expr_analyse_assign_right_side(context, NULL, decl->type, decl->var.init_expr, decl->var.failable || decl->var.unwrap ? FAILABLE_YES : FAILABLE_NO)) return decl_poison(decl); - if (decl->var.unwrap && !decl->var.init_expr->failable) + Expr *init = decl->var.init_expr; + // Handle explicit undef + if (init->expr_kind == EXPR_TYPEINFO && init->type_expr->resolve_status == RESOLVE_DONE + && init->type_expr->type->type_kind == TYPE_VOID) + { + init->expr_kind = EXPR_UNDEF; + init->resolve_status = RESOLVE_DONE; + return true; + } + if (!sema_expr_analyse_assign_right_side(context, NULL, decl->type, init, decl->var.failable || decl->var.unwrap ? FAILABLE_YES : FAILABLE_NO)) return decl_poison(decl); + if (decl->var.unwrap && !init->failable) { SEMA_ERROR(decl->var.init_expr, "A failable expression was expected here."); return decl_poison(decl); diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c index 1d77de9f7..4c120bbb2 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -137,6 +137,9 @@ static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info) case DECL_CT_IF: case DECL_CT_ELIF: case DECL_ATTRIBUTE: + case DECL_CT_SWITCH: + case DECL_CT_CASE: + case DECL_DEFINE: UNREACHABLE } UNREACHABLE diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index d8124df4f..ff0600517 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -38,7 +38,11 @@ const char *attribute_list[NUMBER_OF_ATTRIBUTES]; const char *kw_main; const char *kw_sizeof; +const char *kw_alignof; const char *kw_offsetof; +const char *kw_nameof; +const char *kw_qnameof; +const char *kw_kindof; const char *kw_len; void symtab_init(uint32_t capacity) @@ -75,7 +79,11 @@ void symtab_init(uint32_t capacity) #define KW_DEF(x) symtab_add(x, sizeof(x) - 1, fnv1a(x, sizeof(x) - 1), &type) kw_main = KW_DEF("main"); kw_sizeof = KW_DEF("sizeof"); + kw_alignof = KW_DEF("alignof"); kw_offsetof = KW_DEF("offsetof"); + kw_nameof = KW_DEF("nameof"); + kw_qnameof = KW_DEF("qnameof"); + kw_kindof = KW_DEF("kindof"); kw_len = KW_DEF("len"); attribute_list[ATTRIBUTE_INLINE] = KW_DEF("inline"); attribute_list[ATTRIBUTE_NOINLINE] = KW_DEF("noinline"); diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index f6ae18ce9..1c8145926 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -355,7 +355,6 @@ bool token_is_symbol(TokenType type) { switch (type) { - case TOKEN_MACRO: case TOKEN_CONST: case TOKEN_IDENT: case TOKEN_TYPE_IDENT: diff --git a/src/compiler/types.c b/src/compiler/types.c index bcde01af5..12bb9a8d4 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; +static Type t_voidstar, t_typeid, t_error, t_typeinfo, t_member; Type *type_bool = &t_u1; Type *type_void = &t_u0; @@ -19,6 +19,8 @@ Type *type_voidptr = &t_voidstar; 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; @@ -113,6 +115,10 @@ const char *type_to_error_string(Type *type) return strcat_arena(buffer, ")"); } + case TYPE_MEMBER: + return "member"; + case TYPE_TYPEINFO: + return "typeinfo"; case TYPE_TYPEID: return "typeid"; case TYPE_POINTER: @@ -170,6 +176,8 @@ size_t type_size(Type *type) switch (type->type_kind) { case TYPE_POISONED: + case TYPE_TYPEINFO: + case TYPE_MEMBER: UNREACHABLE; case TYPE_TYPEDEF: return type_size(type->canonical); @@ -202,12 +210,20 @@ size_t type_size(Type *type) UNREACHABLE } +const char *type_generate_qname(Type *type) +{ + if (type_is_builtin(type->type_kind)) return type->name; + return strformat("%s::%s", type->decl->module->name->module, type->name); +} + unsigned int type_abi_alignment(Type *type) { switch (type->type_kind) { case TYPE_POISONED: case TYPE_VOID: + case TYPE_TYPEINFO: + case TYPE_MEMBER: UNREACHABLE; case TYPE_TYPEDEF: return type_abi_alignment(type->canonical); @@ -471,6 +487,8 @@ 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); @@ -681,6 +699,8 @@ Type *type_find_max_type(Type *type, Type *other) case TYPE_POISONED: case TYPE_VOID: case TYPE_BOOL: + case TYPE_TYPEINFO: + case TYPE_MEMBER: return NULL; case TYPE_I8: case TYPE_I16: diff --git a/src/compiler_tests/shorttest.c b/src/compiler_tests/shorttest.c index 138c1fbf6..48a2c135a 100644 --- a/src/compiler_tests/shorttest.c +++ b/src/compiler_tests/shorttest.c @@ -83,7 +83,7 @@ static const char* test_parse = "struct Node\n" " uint success = heap.expand(0x1000);\n" " if (success == 0) \n" " {\n" -" return nil;\n" +" return null;\n" " }\n" " }\n" " else if (wild.size > MAX_WILDERNESS) \n" diff --git a/test/test_suite/bitstruct/bitstruct_general.c3 b/test/test_suite/bitstruct/bitstruct_general.c3 index cf33874e3..38dd42e2b 100644 --- a/test/test_suite/bitstruct/bitstruct_general.c3 +++ b/test/test_suite/bitstruct/bitstruct_general.c3 @@ -1,3 +1,5 @@ +// #skip + module foo; diff --git a/test/test_suite/expressions/arithmetics_sema_fail.c3 b/test/test_suite/expressions/arithmetics_sema_fail.c3 index 1fa43e870..0df3c9bc2 100644 --- a/test/test_suite/expressions/arithmetics_sema_fail.c3 +++ b/test/test_suite/expressions/arithmetics_sema_fail.c3 @@ -74,7 +74,7 @@ func void test14() func void test15() { - nil = nil; // #error: Expression is not assignable + null = null; // #error: Expression is not assignable } func void test16() diff --git a/test/test_suite/expressions/pointer_access.c3t b/test/test_suite/expressions/pointer_access.c3t index a383328a7..d9e3cfa22 100644 --- a/test/test_suite/expressions/pointer_access.c3t +++ b/test/test_suite/expressions/pointer_access.c3t @@ -45,37 +45,39 @@ func void testSimple() entry: %a = alloca %pointer_access.ExtraSimple - %0 = bitcast %pointer_access.ExtraSimple* %a to i8* - call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 72, i1 false) - %c = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2 - %double = getelementptr inbounds %pointer_access.c, %pointer_access.c* %c, i32 0, i32 4 - store double 3.300000e+00, double* %double - %1 = load %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a - %c1 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2 - %j = getelementptr inbounds %pointer_access.c, %pointer_access.c* %c1, i32 0, i32 4 - store double 3.400000e+00, double* %j - %a2 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 0 - %2 = load i32, i32* %a2 - %c3 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2 - %e = getelementptr inbounds %pointer_access.c, %pointer_access.c* %c3, i32 0, i32 0 - %3 = load double, double* %e - %c4 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2 - %f = getelementptr inbounds %pointer_access.c, %pointer_access.c* %c4, i32 0, i32 3 - %4 = load double, double* %f - %c5 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2 - %j6 = getelementptr inbounds %pointer_access.c, %pointer_access.c* %c5, i32 0, i32 4 - %5 = load double, double* %j6 - %g = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 5 - %6 = load i32, i32* %g - %anon = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 4 - %o0 = bitcast %pointer_access.anon.0* %anon to double* - %7 = load double, double* %o0 - %anon7 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 3 - %r = getelementptr inbounds %pointer_access.anon, %pointer_access.anon* %anon7, i32 0, i32 0 - %8 = load i32, i32* %r - %anon8 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 3 - %s = getelementptr inbounds %pointer_access.anon, %pointer_access.anon* %anon8, i32 0, i32 1 - %9 = load i32, i32* %s - call void (i8*, ...) @printf(i8* getelementptr inbounds ([71 x i8], [71 x i8]* @0, i32 0, i32 0), i32 %2, double %3, double %4, double %5, i32 %6, double %7, i32 %8, i32 %9) - ret void + %literal = alloca %pointer_access.ExtraSimple + %0 = bitcast %pointer_access.ExtraSimple* %literal to i8* + call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 72, i1 false) + %c = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %literal, i32 0, i32 2 + %double = getelementptr inbounds %pointer_access.c, %pointer_access.c* %c, i32 0, i32 4 + store double 3.300000e+00, double* %double + %1 = load %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %literal + store %pointer_access.ExtraSimple %1, %pointer_access.ExtraSimple* %a + %c1 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2 + %j = getelementptr inbounds %pointer_access.c, %pointer_access.c* %c1, i32 0, i32 4 + store double 3.400000e+00, double* %j + %a2 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 0 + %2 = load i32, i32* %a2 + %c3 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2 + %e = getelementptr inbounds %pointer_access.c, %pointer_access.c* %c3, i32 0, i32 0 + %3 = load double, double* %e + %c4 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2 + %f = getelementptr inbounds %pointer_access.c, %pointer_access.c* %c4, i32 0, i32 3 + %4 = load double, double* %f + %c5 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2 + %j6 = getelementptr inbounds %pointer_access.c, %pointer_access.c* %c5, i32 0, i32 4 + %5 = load double, double* %j6 + %g = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 5 + %6 = load i32, i32* %g + %anon = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 4 + %o0 = bitcast %pointer_access.anon.0* %anon to double* + %7 = load double, double* %o0 + %anon7 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 3 + %r = getelementptr inbounds %pointer_access.anon, %pointer_access.anon* %anon7, i32 0, i32 0 + %8 = load i32, i32* %r + %anon8 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 3 + %s = getelementptr inbounds %pointer_access.anon, %pointer_access.anon* %anon8, i32 0, i32 1 + %9 = load i32, i32* %s + call void (i8*, ...) @printf(i8* getelementptr inbounds ([71 x i8], [71 x i8]* @0, i32 0, i32 0), i32 %2, double %3, double %4, double %5, i32 %6, double %7, i32 %8, i32 %9) + ret void } \ No newline at end of file diff --git a/test/test_suite/struct/member_access.c3 b/test/test_suite/struct/member_access.c3 index d46245118..211e0075c 100644 --- a/test/test_suite/struct/member_access.c3 +++ b/test/test_suite/struct/member_access.c3 @@ -43,7 +43,7 @@ func void tester() p.bb.b = 2; p.b = 3; p.c = 4; - p.p = nil; + p.p = null; p.d = 5; p.e = 6; p.uu.d = 7; diff --git a/test/test_suite/struct/member_expr.c3 b/test/test_suite/struct/member_expr.c3 index 06609d4b3..3ffff0b72 100644 --- a/test/test_suite/struct/member_expr.c3 +++ b/test/test_suite/struct/member_expr.c3 @@ -58,7 +58,7 @@ func void test_nonstatic_stuct_func1() func void test_nonstatic_stuct_func2() { int a = Foo.func2.sizeof; - int b = Foo.func2(nil, 2); + int b = Foo.func2(null, 2); } */ \ No newline at end of file diff --git a/test/test_suite/struct/struct_codegen.c3t b/test/test_suite/struct/struct_codegen.c3t index a0081abef..b5cc1466b 100644 --- a/test/test_suite/struct/struct_codegen.c3t +++ b/test/test_suite/struct/struct_codegen.c3t @@ -18,10 +18,7 @@ func void test1() @Point = linkonce_odr constant i8 1 entry: - %p = alloca %test.Point - %0 = getelementptr inbounds %test.Point, %test.Point* %p, i32 0, i32 0 - store i32 5, i32* %0 - %1 = getelementptr inbounds %test.Point, %test.Point* %p, i32 0, i32 1 - store i32 6, i32* %1 - %2 = load %test.Point, %test.Point* %p + %p = alloca %test.Point + %0 = load %test.Point, %test.Point* @0 + store %test.Point %0, %test.Point* %p diff --git a/test/test_suite/subarrays/slice_negative_len.c3 b/test/test_suite/subarrays/slice_negative_len.c3 index 957feaa5c..bd8bd7b15 100644 --- a/test/test_suite/subarrays/slice_negative_len.c3 +++ b/test/test_suite/subarrays/slice_negative_len.c3 @@ -58,7 +58,7 @@ func void test9() func void test10() { - int* x = nil; + int* x = null; x[-10..-3]; int[] w = x[0..]; // #error: Omitting end index is not allowed for pointers. int[] z = x[^2..]; // #error: Indexing from the end is not allowed for pointers. diff --git a/test/test_suite/subarrays/slice_offset.c3t b/test/test_suite/subarrays/slice_offset.c3t index 8f2bb14f6..a36f4f23e 100644 --- a/test/test_suite/subarrays/slice_offset.c3t +++ b/test/test_suite/subarrays/slice_offset.c3t @@ -6,17 +6,12 @@ func void test() // #expect: slice_offset.ll -%x = alloca [3 x i32] -%y = alloca %"int[]" -%0 = getelementptr inbounds [3 x i32], [3 x i32]* %x, i32 0, i32 0 -store i32 1, i32* %0 -%1 = getelementptr inbounds [3 x i32], [3 x i32]* %x, i32 0, i32 1 -store i32 2, i32* %1 -%2 = getelementptr inbounds [3 x i32], [3 x i32]* %x, i32 0, i32 2 -store i32 3, i32* %2 -%3 = load [3 x i32], [3 x i32]* %x -%4 = bitcast [3 x i32]* %x to i32* -%offset = getelementptr inbounds i32, i32* %4, i64 1 -%5 = insertvalue %"int[]" undef, i32* %offset, 0 -%6 = insertvalue %"int[]" %5, i64 1, 1 -store %"int[]" %6, %"int[]"* %y + %x = alloca [3 x i32] + %y = alloca %"int[]" + %0 = load [3 x i32], [3 x i32]* @0 + store [3 x i32] %0, [3 x i32]* %x + %1 = bitcast [3 x i32]* %x to i32* + %offset = getelementptr inbounds i32, i32* %1, i64 1 + %2 = insertvalue %"int[]" undef, i32* %offset, 0 + %3 = insertvalue %"int[]" %2, i64 1, 1 + store %"int[]" %3, %"int[]"* %y diff --git a/test/test_suite/subarrays/slice_offset_neg_end.c3t b/test/test_suite/subarrays/slice_offset_neg_end.c3t index 0346ca685..e83462aca 100644 --- a/test/test_suite/subarrays/slice_offset_neg_end.c3t +++ b/test/test_suite/subarrays/slice_offset_neg_end.c3t @@ -6,17 +6,12 @@ func void test() // #expect: slice_offset_neg_end.ll -%x = alloca [3 x i32] -%y = alloca %"int[]" -%0 = getelementptr inbounds [3 x i32], [3 x i32]* %x, i32 0, i32 0 -store i32 1, i32* %0 -%1 = getelementptr inbounds [3 x i32], [3 x i32]* %x, i32 0, i32 1 -store i32 2, i32* %1 -%2 = getelementptr inbounds [3 x i32], [3 x i32]* %x, i32 0, i32 2 -store i32 3, i32* %2 -%3 = load [3 x i32], [3 x i32]* %x -%4 = bitcast [3 x i32]* %x to i32* -%offset = getelementptr inbounds i32, i32* %4, i64 1 -%5 = insertvalue %"int[]" undef, i32* %offset, 0 -%6 = insertvalue %"int[]" %5, i64 1, 1 -store %"int[]" %6, %"int[]"* %y + %x = alloca [3 x i32] + %y = alloca %"int[]" + %0 = load [3 x i32], [3 x i32]* @0 + store [3 x i32] %0, [3 x i32]* %x + %1 = bitcast [3 x i32]* %x to i32* + %offset = getelementptr inbounds i32, i32* %1, i64 1 + %2 = insertvalue %"int[]" undef, i32* %offset, 0 + %3 = insertvalue %"int[]" %2, i64 1, 1 + store %"int[]" %3, %"int[]"* %y diff --git a/test/test_suite/subarrays/slice_offset_neg_start.c3t b/test/test_suite/subarrays/slice_offset_neg_start.c3t index 36fef5b24..6343a9efd 100644 --- a/test/test_suite/subarrays/slice_offset_neg_start.c3t +++ b/test/test_suite/subarrays/slice_offset_neg_start.c3t @@ -6,17 +6,12 @@ func void test() // #expect: slice_offset_neg_start.ll -%x = alloca [3 x i32] -%y = alloca %"int[]" -%0 = getelementptr inbounds [3 x i32], [3 x i32]* %x, i32 0, i32 0 -store i32 1, i32* %0 -%1 = getelementptr inbounds [3 x i32], [3 x i32]* %x, i32 0, i32 1 -store i32 2, i32* %1 -%2 = getelementptr inbounds [3 x i32], [3 x i32]* %x, i32 0, i32 2 -store i32 3, i32* %2 -%3 = load [3 x i32], [3 x i32]* %x -%4 = bitcast [3 x i32]* %x to i32* -%offset = getelementptr inbounds i32, i32* %4, i64 1 -%5 = insertvalue %"int[]" undef, i32* %offset, 0 -%6 = insertvalue %"int[]" %5, i64 1, 1 -store %"int[]" %6, %"int[]"* %y + %x = alloca [3 x i32] + %y = alloca %"int[]" + %0 = load [3 x i32], [3 x i32]* @0 + store [3 x i32] %0, [3 x i32]* %x + %1 = bitcast [3 x i32]* %x to i32* + %offset = getelementptr inbounds i32, i32* %1, i64 1 + %2 = insertvalue %"int[]" undef, i32* %offset, 0 + %3 = insertvalue %"int[]" %2, i64 1, 1 + store %"int[]" %3, %"int[]"* %y diff --git a/test/test_suite/subarrays/slice_start.c3t b/test/test_suite/subarrays/slice_start.c3t index bdf3bb25a..be08e50d6 100644 --- a/test/test_suite/subarrays/slice_start.c3t +++ b/test/test_suite/subarrays/slice_start.c3t @@ -8,15 +8,10 @@ func void test() %x = alloca [3 x i32] %y = alloca %"int[]" -%0 = getelementptr inbounds [3 x i32], [3 x i32]* %x, i32 0, i32 0 -store i32 1, i32* %0 -%1 = getelementptr inbounds [3 x i32], [3 x i32]* %x, i32 0, i32 1 -store i32 2, i32* %1 -%2 = getelementptr inbounds [3 x i32], [3 x i32]* %x, i32 0, i32 2 -store i32 3, i32* %2 -%3 = load [3 x i32], [3 x i32]* %x -%4 = bitcast [3 x i32]* %x to i32* -%offset = getelementptr inbounds i32, i32* %4, i64 0 -%5 = insertvalue %"int[]" undef, i32* %offset, 0 -%6 = insertvalue %"int[]" %5, i64 3, 1 -store %"int[]" %6, %"int[]"* %y +%0 = load [3 x i32], [3 x i32]* @0 +store [3 x i32] %0, [3 x i32]* %x +%1 = bitcast [3 x i32]* %x to i32* +%offset = getelementptr inbounds i32, i32* %1, i64 0 +%2 = insertvalue %"int[]" undef, i32* %offset, 0 +%3 = insertvalue %"int[]" %2, i64 3, 1 +store %"int[]" %3, %"int[]"* %y diff --git a/test/test_suite/subarrays/slice_syntax.c3 b/test/test_suite/subarrays/slice_syntax.c3 index 7b542a6ef..546d51fa5 100644 --- a/test/test_suite/subarrays/slice_syntax.c3 +++ b/test/test_suite/subarrays/slice_syntax.c3 @@ -19,6 +19,6 @@ func void test() flak = flok[^4..5]; flak = flok[2..^2]; flak = flok[^3..^1]; - int* p = nil; + int* p = null; // TODO p[-1..20]; } \ No newline at end of file