diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 3e9781743..4fc676a2d 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -408,9 +408,6 @@ void fprint_type_recursive(Context *context, FILE *file, Type *type, int indent) case TYPE_TYPEINFO: DUMP("(type typeinfo)"); return; - case TYPE_MEMBER: - DUMP("(type member)"); - return; case TYPE_POISONED: DUMP("(type poison)"); return; @@ -622,8 +619,9 @@ 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", TOKSTR(expr->macro_identifier_expr.identifier)); + case EXPR_MACRO_EXPANSION: + DUMP("(macro expansion"); + DUMPEXPR(expr->macro_expansion_expr.inner); DUMPEXPC(expr); DUMPEND(); case EXPR_IDENTIFIER: @@ -638,10 +636,6 @@ void fprint_expr_recursive(Context *context, FILE *file, Expr *expr, int indent) DUMPF("(hashident %s", TOKSTR(expr->hash_ident_expr.identifier)); DUMPEXPC(expr); DUMPEND(); - case EXPR_MACRO_CT_IDENTIFIER: - DUMPF("(macroctident @%s", TOKSTR(expr->ct_ident_expr.identifier)); - DUMPEXPC(expr); - DUMPEND(); case EXPR_CONST_IDENTIFIER: DUMPF("(ident %s", TOKSTR(expr->identifier_expr.identifier)); DUMPEXPC(expr); @@ -693,9 +687,10 @@ void fprint_expr_recursive(Context *context, FILE *file, Expr *expr, int indent) DUMPEXPR(expr->trycatch_expr); DUMPEND(); case EXPR_ACCESS: - DUMPF("(access .%s", TOKSTR(expr->access_expr.sub_element)); + DUMP("(access"); DUMPEXPC(expr); DUMPEXPR(expr->access_expr.parent); + DUMPEXPR(expr->access_expr.child); DUMPEND(); case EXPR_TYPEID: DUMP("(typeid"); @@ -905,16 +900,16 @@ void fprint_decl_recursive(Context *context, FILE *file, Decl *decl, int indent) DUMPEND(); case DECL_FUNC: DUMPF("(func %s", decl->name); - if (decl->func.type_parent) + if (decl->func_decl.type_parent) { indent++; DUMP("(parent_type"); - DUMPTI(decl->func.type_parent); + DUMPTI(decl->func_decl.type_parent); DUMPE(); indent--; } - fprint_func_signature(context, file, &decl->func.function_signature, indent + 1); - if (decl->func.body) DUMPAST(decl->func.body); + fprint_func_signature(context, file, &decl->func_decl.function_signature, indent + 1); + if (decl->func_decl.body) DUMPAST(decl->func_decl.body); DUMPEND(); case DECL_STRUCT: DUMPF("(struct %s", decl->name ? decl->name : "anon"); diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 491a0a2e7..f8955487e 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -36,6 +36,7 @@ typedef struct #define NO_TOKEN ((Token) { .type = TOKEN_INVALID_TOKEN }) #define INVALID_TOKEN_ID ((TokenId) { UINT32_MAX }) #define INVALID_RANGE ((SourceSpan){ INVALID_TOKEN_ID, INVALID_TOKEN_ID }) +#define TOKEN_IS_INVALID(_token_id) ((_token_id).index == INVALID_TOKEN_ID.index) #define MAX_LOCALS 0xFFF #define MAX_SCOPE_DEPTH 0x100 #define MAX_STRING_BUFFER 0x10000 @@ -415,6 +416,7 @@ typedef struct bool failable : 1; Decl **parameters; Decl **body_parameters; + TypeInfo *type_parent; // May be null TypeInfo *rtype; // May be null! struct Ast_ *body; } MacroDecl; @@ -508,7 +510,7 @@ typedef struct Decl_ { struct { - Decl** methods; + Decl **methods; union { // Unions, Errtype and Struct use strukt @@ -520,7 +522,7 @@ typedef struct Decl_ VarDecl var; LabelDecl label; EnumConstantDecl enum_constant; - FuncDecl func; + FuncDecl func_decl; AttrDecl attr; TypedefDecl typedef_decl; InterfaceDecl interface_decl; @@ -629,7 +631,7 @@ typedef struct Expr *parent; union { - TokenId sub_element; + Expr *child; Decl *ref; }; } ExprAccess; @@ -718,6 +720,12 @@ typedef struct Decl *decl; } ExprIdentifierRaw; +typedef struct +{ + Expr *inner; + Decl *decl; +} ExprMacroExpansion; + typedef struct { CastKind kind; @@ -826,6 +834,7 @@ struct Expr_ ExprIdentifier macro_identifier_expr; ExprIdentifierRaw ct_ident_expr; ExprIdentifierRaw ct_macro_ident_expr; + ExprMacroExpansion macro_expansion_expr; ExprIdentifierRaw hash_ident_expr; TypeInfo *typeid_expr; ExprInitializer initializer_expr; @@ -1272,6 +1281,7 @@ typedef struct Context_ Decl **interfaces; Decl **templates; Decl **methods; + Decl **macro_methods; Decl **vars; Decl **incr_array; Decl **ct_ifs; diff --git a/src/compiler/context.c b/src/compiler/context.c index eba757701..3f54697e9 100644 --- a/src/compiler/context.c +++ b/src/compiler/context.c @@ -118,11 +118,19 @@ void context_register_global_decl(Context *context, Decl *decl) decl_set_external_name(decl); break; case DECL_MACRO: - vec_add(context->macros, decl); + if (decl->macro_decl.type_parent) + { + vec_add(context->macro_methods, decl); + return; + } + else + { + vec_add(context->macros, decl); + } decl_set_external_name(decl); break; case DECL_FUNC: - if (decl->func.type_parent) + if (decl->func_decl.type_parent) { vec_add(context->methods, decl); return; diff --git a/src/compiler/copying.c b/src/compiler/copying.c index b0653ead4..a61c9a116 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -76,12 +76,13 @@ Expr *copy_expr(Expr *source_expr) return expr; case EXPR_PLACEHOLDER: case EXPR_CONST_IDENTIFIER: - case EXPR_MACRO_IDENTIFIER: case EXPR_CT_IDENT: - case EXPR_MACRO_CT_IDENTIFIER: case EXPR_HASH_IDENT: // TODO return expr; + case EXPR_MACRO_EXPANSION: + MACRO_COPY_EXPR(expr->macro_expansion_expr.inner); + return expr; case EXPR_DESIGNATOR: expr->designator_expr.path = macro_copy_designator_list(expr->designator_expr.path); MACRO_COPY_EXPR(expr->designator_expr.value); @@ -406,6 +407,7 @@ TypeInfo *copy_type_info(TypeInfo *source) { if (!source) return NULL; TypeInfo *copy = type_info_copy(source); + if (source->resolve_status == RESOLVE_DONE) return copy; switch (source->kind) { case TYPE_INFO_POISON: @@ -428,7 +430,6 @@ TypeInfo *copy_type_info(TypeInfo *source) copy->array.base = copy_type_info(source->array.base); return copy; case TYPE_INFO_POINTER: - assert(source->resolve_status == RESOLVE_NOT_DONE); copy->pointer = copy_type_info(source->pointer); return copy; } @@ -496,10 +497,10 @@ Decl *copy_decl(Decl *decl) MACRO_COPY_DECL_LIST(copy->interface_decl.functions); break; case DECL_FUNC: - MACRO_COPY_TYPE(copy->func.type_parent); - copy->func.annotations = NULL; - copy_function_signature_deep(©->func.function_signature); - MACRO_COPY_AST(copy->func.body); + MACRO_COPY_TYPE(copy->func_decl.type_parent); + copy->func_decl.annotations = NULL; + copy_function_signature_deep(©->func_decl.function_signature); + MACRO_COPY_AST(copy->func_decl.body); break; case DECL_VAR: MACRO_COPY_TYPE(copy->var.type_info); @@ -557,6 +558,7 @@ Decl *copy_decl(Decl *decl) case DECL_ARRAY_VALUE: TODO case DECL_MACRO: + MACRO_COPY_TYPE(decl->macro_decl.type_parent); MACRO_COPY_DECL_LIST(decl->macro_decl.parameters); MACRO_COPY_AST(decl->macro_decl.body); MACRO_COPY_TYPE(decl->macro_decl.rtype); diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 3bd01be48..904a59299 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -194,8 +194,7 @@ typedef enum EXPR_GUARD, EXPR_HASH_IDENT, EXPR_MACRO_BLOCK, - EXPR_MACRO_CT_IDENTIFIER, - EXPR_MACRO_IDENTIFIER, + EXPR_MACRO_EXPANSION, EXPR_MEMBER_ACCESS, EXPR_IDENTIFIER, EXPR_INITIALIZER_LIST, @@ -242,8 +241,10 @@ typedef enum PREC_BIT, // ^ | & PREC_SHIFT, // << >> >>> PREC_MULTIPLICATIVE, // * / % - PREC_UNARY, // @ ! - + ~ * & prefix ++/-- + PREC_UNARY, // ! - + ~ * & prefix ++/-- PREC_CALL, // . () [] postfix ++/-- + PREC_MACRO, + PREC_FIRST = PREC_MACRO } Precedence; typedef enum @@ -512,11 +513,10 @@ typedef enum TYPE_SUBARRAY, TYPE_INFERRED_ARRAY, TYPE_TYPEINFO, - TYPE_MEMBER, TYPE_VECTOR, TYPE_VIRTUAL, TYPE_VIRTUAL_ANY, - TYPE_LAST = TYPE_MEMBER + TYPE_LAST = TYPE_VIRTUAL_ANY } TypeKind; #define ALL_INTS TYPE_I8: case TYPE_I16: case TYPE_I32: case TYPE_I64: case TYPE_I128: \ diff --git a/src/compiler/headers.c b/src/compiler/headers.c index ec0b4d4fa..c11ac2219 100644 --- a/src/compiler/headers.c +++ b/src/compiler/headers.c @@ -121,8 +121,6 @@ static void header_print_type(FILE *file, Type *type) break; case TYPE_TYPEINFO: break; - case TYPE_MEMBER: - break; case TYPE_VECTOR: break; } diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index b57c13219..3c6ea89f9 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -978,12 +978,12 @@ void *llvm_gen(Module *module) VECEACH(context->functions, i) { Decl *decl = context->functions[i]; - if (decl->func.body) llvm_emit_function_body(gen_context, decl); + if (decl->func_decl.body) llvm_emit_function_body(gen_context, decl); } VECEACH(context->methods, i) { Decl *decl = context->methods[i]; - if (decl->func.body) llvm_emit_function_body(gen_context, decl); + if (decl->func_decl.body) llvm_emit_function_body(gen_context, decl); } gencontext_end_file_emit(gen_context, context); diff --git a/src/compiler/llvm_codegen_c_abi_x64.c b/src/compiler/llvm_codegen_c_abi_x64.c index 642c3fa31..398dd39a5 100644 --- a/src/compiler/llvm_codegen_c_abi_x64.c +++ b/src/compiler/llvm_codegen_c_abi_x64.c @@ -401,7 +401,6 @@ static void x64_classify(Type *type, ByteSize offset_base, X64Class *lo_class, X case TYPE_TYPEID: case TYPE_FUNC: case TYPE_TYPEINFO: - case TYPE_MEMBER: case TYPE_DISTINCT: case TYPE_STRLIT: case TYPE_INFERRED_ARRAY: @@ -603,7 +602,6 @@ AbiType *x64_get_int_type_at_offset(Type *type, unsigned offset, Type *source_ty case TYPE_FUNC: case TYPE_TYPEDEF: case TYPE_TYPEINFO: - case TYPE_MEMBER: case TYPE_DISTINCT: case TYPE_STRLIT: case TYPE_INFERRED_ARRAY: diff --git a/src/compiler/llvm_codegen_c_abi_x86.c b/src/compiler/llvm_codegen_c_abi_x86.c index 1b1e1dd8d..3aeed0d34 100644 --- a/src/compiler/llvm_codegen_c_abi_x86.c +++ b/src/compiler/llvm_codegen_c_abi_x86.c @@ -109,7 +109,6 @@ static bool x86_should_return_type_in_reg(Type *type) { case TYPE_VECTOR: case TYPE_POISONED: - case TYPE_MEMBER: case TYPE_VOID: case TYPE_FUNC: case TYPE_TYPEDEF: @@ -617,7 +616,6 @@ static ABIArgInfo *x86_classify_argument(CallConvention call, Regs *regs, Type * case TYPE_ERR_UNION: return x86_classify_aggregate(call, regs, type); case TYPE_TYPEINFO: - case TYPE_MEMBER: UNREACHABLE } UNREACHABLE diff --git a/src/compiler/llvm_codegen_debug_info.c b/src/compiler/llvm_codegen_debug_info.c index 95a9e4d5e..4d35968be 100644 --- a/src/compiler/llvm_codegen_debug_info.c +++ b/src/compiler/llvm_codegen_debug_info.c @@ -91,7 +91,7 @@ void llvm_emit_debug_global_var(GenContext *c, Decl *global) void llvm_emit_debug_function(GenContext *c, Decl *decl) { LLVMDIFlags flags = LLVMDIFlagZero; - if (!decl->func.body) return; + if (!decl->func_decl.body) return; switch (decl->visibility) { case VISIBLE_LOCAL: @@ -108,7 +108,7 @@ void llvm_emit_debug_function(GenContext *c, Decl *decl) UNREACHABLE } flags |= LLVMDIFlagPrototyped; - if (decl->func.attr_noreturn) flags |= LLVMDIFlagNoReturn; + if (decl->func_decl.attr_noreturn) flags |= LLVMDIFlagNoReturn; SourceLocation *loc = TOKLOC(decl->span.loc); c->debug.function = LLVMDIBuilderCreateFunction(c->debug.builder, @@ -488,7 +488,6 @@ static inline LLVMMetadataRef llvm_get_debug_type_internal(GenContext *c, Type * case TYPE_FXX: case TYPE_TYPEID: case TYPE_TYPEINFO: - case TYPE_MEMBER: case TYPE_INFERRED_ARRAY: case TYPE_STRLIT: UNREACHABLE diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index bf22d9d16..a3393c5d3 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -2262,7 +2262,6 @@ static void llvm_expand_type_to_args(GenContext *context, Type *param_type, LLVM case TYPE_TYPEID: case TYPE_FUNC: case TYPE_TYPEINFO: - case TYPE_MEMBER: case TYPE_DISTINCT: case TYPE_STRLIT: case TYPE_INFERRED_ARRAY: @@ -2564,7 +2563,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *be_value, Expr *expr) Decl *function_decl = expr->call_expr.function->access_expr.ref; // 2b. Set signature, function and function type - signature = &function_decl->func.function_signature; + signature = &function_decl->func_decl.function_signature; func = function_decl->backend_ref; assert(func); func_type = llvm_get_type(c, function_decl->type); @@ -2576,14 +2575,14 @@ void llvm_emit_call_expr(GenContext *c, BEValue *be_value, Expr *expr) function_decl = decl_flatten(function_decl); // 3a. This may be an intrinsic, if so generate an intrinsic call instead. - if (function_decl->func.is_builtin) + if (function_decl->func_decl.is_builtin) { gencontext_emit_call_intrinsic_expr(c, be_value, expr); return; } // 3b. Set signature, function and function type - signature = &function_decl->func.function_signature; + signature = &function_decl->func_decl.function_signature; func = function_decl->backend_ref; func_type = llvm_get_type(c, function_decl->type); } @@ -3004,6 +3003,7 @@ static inline void llvm_emit_macro_block(GenContext *context, BEValue *be_value, } llvm_emit_and_set_decl_alloca(context, decl); BEValue value; + llvm_emit_expr(context, &value, expr->macro_block.args[i]); llvm_store_aligned_decl(context, decl, llvm_value_rvalue_store(context, &value)); } @@ -3109,8 +3109,7 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) case EXPR_DECL_LIST: case EXPR_TYPEINFO: case EXPR_ENUM_CONSTANT: - case EXPR_MACRO_IDENTIFIER: - case EXPR_MACRO_CT_IDENTIFIER: + case EXPR_MACRO_EXPANSION: case EXPR_CT_IDENT: case EXPR_HASH_IDENT: case EXPR_PLACEHOLDER: diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index c970ce3dc..1c9a5c19f 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -242,7 +242,7 @@ static inline void llvm_emit_return_value(GenContext *context, LLVMValueRef valu void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failable) { - FunctionSignature *signature = &c->cur_func_decl->func.function_signature; + FunctionSignature *signature = &c->cur_func_decl->func_decl.function_signature; ABIArgInfo *info = signature->ret_abi_info; // If we have a failable it's always the return argument, so we need to copy @@ -344,12 +344,12 @@ void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failabl void llvm_emit_return_implicit(GenContext *c) { - if (c->cur_func_decl->func.function_signature.rtype->type != type_void) + if (c->cur_func_decl->func_decl.function_signature.rtype->type != type_void) { LLVMBuildUnreachable(c->builder); return; } - if (!c->cur_func_decl->func.function_signature.failable) + if (!c->cur_func_decl->func_decl.function_signature.failable) { llvm_emit_return_abi(c, NULL, NULL); return; @@ -390,7 +390,7 @@ void llvm_emit_function_body(GenContext *context, Decl *decl) LLVMValueRef alloca_point = LLVMBuildAlloca(context->builder, LLVMInt32TypeInContext(context->context), "alloca_point"); context->alloca_point = alloca_point; - FunctionSignature *signature = &decl->func.function_signature; + FunctionSignature *signature = &decl->func_decl.function_signature; unsigned arg = 0; if (emit_debug) @@ -421,16 +421,16 @@ void llvm_emit_function_body(GenContext *context, Decl *decl) // Generate LLVMValueRef's for all parameters, so we can use them as local vars in code - VECEACH(decl->func.function_signature.params, i) + VECEACH(decl->func_decl.function_signature.params, i) { - llvm_emit_parameter(context, decl->func.function_signature.params[i], &arg, i); + llvm_emit_parameter(context, decl->func_decl.function_signature.params[i], &arg, i); } LLVMSetCurrentDebugLocation2(context->builder, NULL); - VECEACH(decl->func.body->compound_stmt.stmts, i) + VECEACH(decl->func_decl.body->compound_stmt.stmts, i) { - llvm_emit_stmt(context, decl->func.body->compound_stmt.stmts[i]); + llvm_emit_stmt(context, decl->func_decl.body->compound_stmt.stmts[i]); } if (context->current_block && !LLVMGetFirstInstruction(context->current_block) && !LLVMGetFirstUse(LLVMBasicBlockAsValue(context->current_block))) @@ -443,8 +443,8 @@ void llvm_emit_function_body(GenContext *context, Decl *decl) // Insert a return (and defer) if needed. if (context->current_block && !LLVMGetBasicBlockTerminator(context->current_block)) { - assert(!decl->func.body->compound_stmt.defer_list.end); - llvm_emit_defer(context, decl->func.body->compound_stmt.defer_list.start, 0); + assert(!decl->func_decl.body->compound_stmt.defer_list.end); + llvm_emit_defer(context, decl->func_decl.body->compound_stmt.defer_list.start, 0); llvm_emit_return_implicit(context); } @@ -524,7 +524,7 @@ void llvm_emit_function_decl(GenContext *c, Decl *decl) // Resolve function backend type for function. LLVMValueRef function = LLVMAddFunction(c->module, decl->cname ?: decl->external_name, llvm_get_type(c, decl->type)); decl->backend_ref = function; - FunctionSignature *signature = &decl->func.function_signature; + FunctionSignature *signature = &decl->func_decl.function_signature; FunctionSignature *type_signature = decl->type->func.signature; // We only resolve 1 function signature, so we might have functions @@ -560,15 +560,15 @@ void llvm_emit_function_decl(GenContext *c, Decl *decl) ABIArgInfo *info = param->var.abi_info; llvm_emit_param_attributes(c, function, info, false, info->param_index_start + 1, info->param_index_end); } - if (decl->func.attr_inline) + if (decl->func_decl.attr_inline) { llvm_attribute_add(c, function, attribute_alwaysinline, -1); } - if (decl->func.attr_noinline) + if (decl->func_decl.attr_noinline) { llvm_attribute_add(c, function, attribute_noinline, -1); } - if (decl->func.attr_noreturn) + if (decl->func_decl.attr_noreturn) { llvm_attribute_add(c, function, attribute_noreturn, -1); } @@ -582,7 +582,7 @@ void llvm_emit_function_decl(GenContext *c, Decl *decl) } llvm_attribute_add(c, function, attribute_nounwind, -1); - if (decl->func.attr_stdcall && (platform_target.os == OS_TYPE_WIN32)) + if (decl->func_decl.attr_stdcall && (platform_target.os == OS_TYPE_WIN32)) { LLVMSetFunctionCallConv(function, LLVMX86StdcallCallConv); LLVMSetDLLStorageClass(function, LLVMDLLImportStorageClass); @@ -591,16 +591,16 @@ void llvm_emit_function_decl(GenContext *c, Decl *decl) switch (decl->visibility) { case VISIBLE_EXTERN: - LLVMSetLinkage(function, decl->func.attr_weak ? LLVMExternalWeakLinkage : LLVMExternalLinkage); + LLVMSetLinkage(function, decl->func_decl.attr_weak ? LLVMExternalWeakLinkage : LLVMExternalLinkage); LLVMSetVisibility(function, LLVMDefaultVisibility); break; case VISIBLE_PUBLIC: case VISIBLE_MODULE: - if (decl->func.attr_weak) LLVMSetLinkage(function, LLVMWeakAnyLinkage); + if (decl->func_decl.attr_weak) LLVMSetLinkage(function, LLVMWeakAnyLinkage); LLVMSetVisibility(function, LLVMDefaultVisibility); break; case VISIBLE_LOCAL: - LLVMSetLinkage(function, decl->func.attr_weak ? LLVMLinkerPrivateWeakLinkage : LLVMInternalLinkage); + LLVMSetLinkage(function, decl->func_decl.attr_weak ? LLVMLinkerPrivateWeakLinkage : LLVMInternalLinkage); LLVMSetVisibility(function, LLVMDefaultVisibility); break;; } @@ -611,6 +611,15 @@ void llvm_emit_function_decl(GenContext *c, Decl *decl) } +void llvm_emit_methods(GenContext *c, Decl **methods) +{ + VECEACH(methods, i) + { + Decl *decl = methods[i]; + if (decl->decl_kind == DECL_MACRO) continue; + llvm_emit_function_decl(c, decl); + } +} void llvm_emit_extern_decl(GenContext *context, Decl *decl) { @@ -630,18 +639,12 @@ void llvm_emit_extern_decl(GenContext *context, Decl *decl) case DECL_STRUCT: case DECL_UNION: case DECL_ERR: - VECEACH(decl->methods, i) - { - llvm_emit_function_decl(context, decl->methods[i]); - } + llvm_emit_methods(context, decl->methods); llvm_get_type(context, decl->type); // TODO // Fix typeid break; case DECL_ENUM: - VECEACH(decl->methods, i) - { - llvm_emit_function_decl(context, decl->methods[i]); - } + llvm_emit_methods(context, decl->methods); // TODO // Fix typeid return; case DECL_TYPEDEF: diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 1a20596c2..714f4634b 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -180,7 +180,7 @@ static inline void gencontext_emit_return(GenContext *c, Ast *ast) c->error_var = c->block_error_var; c->catch_block = c->block_failable_exit; } - else if (c->cur_func_decl->func.function_signature.failable) + else if (c->cur_func_decl->func_decl.function_signature.failable) { error_return_block = llvm_basic_block_new(c, "err_retblock"); error_out = llvm_emit_alloca_aligned(c, type_error, "reterr"); diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index be8749efa..be30d198f 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -18,15 +18,15 @@ static inline LLVMTypeRef llvm_type_from_decl(GenContext *c, Decl *decl) UNREACHABLE case DECL_FUNC: { - VECEACH(decl->func.function_signature.params, i) + VECEACH(decl->func_decl.function_signature.params, i) { - params[i] = llvm_get_type(c, decl->func.function_signature.params[i]->type); + params[i] = llvm_get_type(c, decl->func_decl.function_signature.params[i]->type); } - unsigned param_size = vec_size(decl->func.function_signature.params); - return LLVMFunctionType(llvm_get_type(c, decl->func.function_signature.rtype->type), + unsigned param_size = vec_size(decl->func_decl.function_signature.params); + return LLVMFunctionType(llvm_get_type(c, decl->func_decl.function_signature.rtype->type), params, param_size, - decl->func.function_signature.variadic); + decl->func_decl.function_signature.variadic); } case DECL_TYPEDEF: @@ -334,7 +334,6 @@ LLVMTypeRef llvm_get_type(GenContext *c, Type *any_type) { case TYPE_POISONED: case TYPE_TYPEINFO: - case TYPE_MEMBER: case TYPE_INFERRED_ARRAY: UNREACHABLE case TYPE_TYPEID: diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 4d64b03e4..7d27f5caa 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -179,26 +179,16 @@ bool parse_param_list(Context *context, Expr ***result, TokenType param_end, boo } } -static Expr *parse_macro_ident(Context *context, Expr *left) +static Expr *parse_macro_expansion(Context *context, Expr *left) { assert(!left && "Unexpected left hand side"); - Expr *macro_ident = EXPR_NEW_TOKEN(EXPR_MACRO_IDENTIFIER, context->tok); + Expr *macro_expression = EXPR_NEW_TOKEN(EXPR_MACRO_EXPANSION, context->tok); advance_and_verify(context, TOKEN_AT); - if (TOKEN_IS(TOKEN_CT_IDENT)) - { - macro_ident->ct_macro_ident_expr.identifier = context->tok.id; - 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; - macro_ident->identifier_expr.identifier = context->tok.id; - CONSUME_OR(TOKEN_IDENT, poisoned_expr); - RANGE_EXTEND_PREV(macro_ident); - return macro_ident; + Expr *inner = TRY_EXPR_OR(parse_precedence(context, PREC_MACRO), poisoned_expr); + macro_expression->macro_expansion_expr.inner = inner; + assert(inner); + RANGE_EXTEND_PREV(macro_expression); + return macro_expression; } @@ -460,7 +450,7 @@ static Expr *parse_call_expr(Context *context, Expr *left) { if (!parse_param_list(context, ¶ms, TOKEN_RPAREN, &unsplat)) return poisoned_expr; } - if (try_consume(context, TOKEN_EOS) && left->expr_kind == EXPR_MACRO_IDENTIFIER) + if (try_consume(context, TOKEN_EOS) && left->expr_kind == EXPR_MACRO_EXPANSION) { if (!parse_macro_argument_declarations(context, VISIBLE_LOCAL, &body_args, false)) return poisoned_expr; } @@ -482,7 +472,7 @@ static Expr *parse_call_expr(Context *context, Expr *left) SEMA_TOKEN_ERROR(context->tok, "Expected a macro body here."); return poisoned_expr; } - if (TOKEN_IS(TOKEN_LBRACE) && left->expr_kind == EXPR_MACRO_IDENTIFIER) + if (TOKEN_IS(TOKEN_LBRACE) && left->expr_kind == EXPR_MACRO_EXPANSION) { call->call_expr.body = TRY_AST_OR(parse_compound_stmt(context), poisoned_expr); } @@ -555,16 +545,8 @@ static Expr *parse_access_expr(Context *context, Expr *left) advance_and_verify(context, TOKEN_DOT); Expr *access_expr = EXPR_NEW_EXPR(EXPR_ACCESS, left); access_expr->access_expr.parent = left; - access_expr->access_expr.sub_element = context->tok.id; - 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; + access_expr->access_expr.child = TRY_EXPR_OR(parse_precedence(context, PREC_CALL + 1), poisoned_expr); + RANGE_EXTEND_PREV(access_expr); return access_expr; } @@ -1099,7 +1081,7 @@ ParseRule rules[TOKEN_EOF + 1] = { [TOKEN_INTEGER] = { parse_integer, NULL, PREC_NONE }, [TOKEN_PLACEHOLDER] = { parse_placeholder, NULL, PREC_NONE }, [TOKEN_CHAR_LITERAL] = { parse_char_lit, NULL, PREC_NONE }, - [TOKEN_AT] = { parse_macro_ident, NULL, PREC_NONE }, + [TOKEN_AT] = { parse_macro_expansion, 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 }, diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 5c695df4e..cb2a812c5 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -1768,12 +1768,31 @@ static inline Decl *parse_macro_declaration(Context *context, Visibility visibil Decl *decl = decl_new(DECL_MACRO, context->tok.id, visibility); decl->macro_decl.rtype = rtype; decl->macro_decl.failable = failable; - if (rtype && TOKEN_IS(TOKEN_DOT)) + if (rtype) { - SEMA_ERROR(rtype, "Expected a macro name here."); - return poisoned_decl; + if (TOKEN_IS(TOKEN_DOT)) + { + if (failable) + { + SEMA_ERROR(rtype, "Expected a macro name here."); + return poisoned_decl; + } + advance(context); + decl->macro_decl.type_parent = rtype; + decl->macro_decl.rtype = NULL; + } + else + { + if (parse_next_is_type(context)) + { + decl->macro_decl.type_parent = TRY_TYPE_OR(parse_type(context), poisoned_decl); + TRY_CONSUME_OR(TOKEN_DOT, "Did you forget a ',' here?", poisoned_decl); + } + } } + decl->name_token = context->tok.id; + decl->name = TOKSTR(context->tok); TRY_CONSUME_OR(TOKEN_IDENT, "Expected a macro name here.", poisoned_decl); bool trailing_body = false; if (!parse_macro_arguments(context, visibility, &decl->macro_decl.parameters, &decl->macro_decl.body_parameters, &trailing_body)) return poisoned_decl; @@ -1953,8 +1972,8 @@ static inline Decl *parse_func_definition(Context *context, Visibility visibilit { Decl *func = decl_new(DECL_FUNC, context->next_tok.id, visibility); advance_and_verify(context, TOKEN_FUNC); - func->func.function_signature.rtype = TRY_TYPE_OR(parse_type(context), poisoned_decl); - func->func.function_signature.failable = try_consume(context, TOKEN_BANG); + func->func_decl.function_signature.rtype = TRY_TYPE_OR(parse_type(context), poisoned_decl); + func->func_decl.function_signature.failable = try_consume(context, TOKEN_BANG); SourceSpan start = source_span_from_token_id(context->tok.id); bool had_error = false; Path *path = parse_path_prefix(context, &had_error); @@ -1967,7 +1986,7 @@ static inline Decl *parse_func_definition(Context *context, Visibility visibilit TypeInfo *type = type_info_new(TYPE_INFO_IDENTIFIER, start); type->unresolved.path = path; type->unresolved.name_loc = context->tok.id; - func->func.type_parent = type; + func->func_decl.type_parent = type; advance_and_verify(context, TOKEN_TYPE_IDENT); TRY_CONSUME_OR(TOKEN_DOT, "Expected '.' after the type in a method declaration.", poisoned_decl); @@ -1978,7 +1997,7 @@ static inline Decl *parse_func_definition(Context *context, Visibility visibilit func->name_token = context->tok.id; advance_and_verify(context, TOKEN_IDENT); RANGE_EXTEND_PREV(func); - if (!parse_opt_parameter_type_list(context, visibility, &(func->func.function_signature), is_interface)) return poisoned_decl; + if (!parse_opt_parameter_type_list(context, visibility, &(func->func_decl.function_signature), is_interface)) return poisoned_decl; if (!parse_attributes(context, func)) return poisoned_decl; @@ -1998,7 +2017,7 @@ static inline Decl *parse_func_definition(Context *context, Visibility visibilit TRY_EXPECT_OR(TOKEN_LBRACE, "Expected the beginning of a block with '{'", poisoned_decl); - func->func.body = TRY_AST_OR(parse_compound_stmt(context), poisoned_decl); + func->func_decl.body = TRY_AST_OR(parse_compound_stmt(context), poisoned_decl); DEBUG_LOG("Finished parsing function %s", func->name); return func; diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index fd26d3602..c19142967 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -379,7 +379,6 @@ CastKind cast_to_bool_kind(Type *type) case TYPE_ARRAY: case TYPE_TYPEID: case TYPE_TYPEINFO: - case TYPE_MEMBER: case TYPE_VECTOR: return CAST_ERROR; } @@ -402,7 +401,6 @@ bool cast_may_explicit(Type *from_type, Type *to_type) case TYPE_POISONED: case TYPE_VOID: case TYPE_TYPEINFO: - case TYPE_MEMBER: case TYPE_DISTINCT: case TYPE_FUNC: case TYPE_TYPEDEF: @@ -715,7 +713,6 @@ bool cast(Expr *expr, Type *to_type) case TYPE_VOID: case TYPE_TYPEID: case TYPE_TYPEINFO: - case TYPE_MEMBER: case TYPE_DISTINCT: case TYPE_FUNC: case TYPE_TYPEDEF: diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 1871ded64..2331ee8c5 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -286,7 +286,7 @@ static bool sema_analyse_struct_union(Context *context, Decl *decl) if (attribute == ATTRIBUTE_NONE) return decl_poison(decl); bool had = false; -#define SET_ATTR(_X) had = decl->func._X; decl->func._X = true; break +#define SET_ATTR(_X) had = decl->func_decl._X; decl->func_decl._X = true; break switch (attribute) { case ATTRIBUTE_CNAME: @@ -471,7 +471,6 @@ static inline bool sema_analyse_distinct(Context *context, Decl *decl) case TYPE_DISTINCT: case TYPE_INFERRED_ARRAY: case TYPE_TYPEINFO: - case TYPE_MEMBER: UNREACHABLE return false; case TYPE_VIRTUAL_ANY: @@ -589,13 +588,13 @@ static inline bool sema_analyse_enum(Context *context, Decl *decl) static inline bool sema_analyse_method(Context *context, Decl *decl) { - TypeInfo *parent_type = decl->func.type_parent; + TypeInfo *parent_type = decl->func_decl.type_parent; if (!sema_resolve_type_info(context, parent_type)) return false; if (!type_may_have_sub_elements(parent_type->type)) { SEMA_ERROR(decl, "Methods can not be associated with '%s'", - type_to_error_string(decl->func.type_parent->type)); + type_to_error_string(decl->func_decl.type_parent->type)); return false; } Decl *parent = parent_type->type->decl; @@ -764,10 +763,10 @@ static AttributeType sema_analyse_attribute(Context *context, Attr *attr, Attrib static inline bool sema_analyse_func(Context *context, Decl *decl) { DEBUG_LOG("----Analysing function %s", decl->name); - Type *func_type = sema_analyse_function_signature(context, &decl->func.function_signature, true); + Type *func_type = sema_analyse_function_signature(context, &decl->func_decl.function_signature, true); decl->type = func_type; if (!func_type) return decl_poison(decl); - if (decl->func.type_parent) + if (decl->func_decl.type_parent) { if (!sema_analyse_method(context, decl)) return decl_poison(decl); } @@ -792,7 +791,7 @@ static inline bool sema_analyse_func(Context *context, Decl *decl) if (attribute == ATTRIBUTE_NONE) return decl_poison(decl); bool had = false; -#define SET_ATTR(_X) had = decl->func._X; decl->func._X = true; break +#define SET_ATTR(_X) had = decl->func_decl._X; decl->func_decl._X = true; break switch (attribute) { case ATTRIBUTE_CNAME: @@ -821,7 +820,7 @@ static inline bool sema_analyse_func(Context *context, Decl *decl) SEMA_TOKID_ERROR(attr->name, "Attribute occurred twice, please remove one."); return decl_poison(decl); } - if (decl->func.attr_inline && decl->func.attr_noinline) + if (decl->func_decl.attr_inline && decl->func_decl.attr_noinline) { SEMA_TOKID_ERROR(attr->name, "A function cannot be 'inline' and 'noinline' at the same time."); return decl_poison(decl); @@ -831,10 +830,60 @@ static inline bool sema_analyse_func(Context *context, Decl *decl) return true; } +static bool sema_analyse_macro_method(Context *context, Decl *decl) +{ + if (!sema_resolve_type_info(context, decl->macro_decl.type_parent)) return false; + Type *parent_type = decl->macro_decl.type_parent->type; + if (!type_may_have_sub_elements(parent_type)) + { + SEMA_ERROR(decl->macro_decl.type_parent, + "Methods can not be associated with '%s'", + type_to_error_string(parent_type)); + return false; + } + Type *expected_first_element = type_get_ptr(parent_type); + if (!vec_size(decl->macro_decl.parameters)) + { + SEMA_ERROR(decl, "Expected at least one parameter - of type %s.", type_quoted_error_string(expected_first_element)); + return false; + } + Decl *first_param = decl->macro_decl.parameters[0]; + if (!first_param->type) + { + SEMA_ERROR(first_param, "The first parameter must have the explicit type %s.", type_quoted_error_string(expected_first_element)); + return false; + } + if (first_param->type->canonical != expected_first_element->canonical) + { + SEMA_ERROR(first_param, "The first parameter must be of type %s.", expected_first_element); + return false; + } + if (first_param->var.kind != VARDECL_PARAM) + { + SEMA_ERROR(first_param, "The first parameter must be a regular value parameter."); + return false; + } + Decl *parent = parent_type->decl; + VECEACH(parent->methods, i) + { + Decl *function = parent->methods[i]; + if (function->name == decl->name) + { + SEMA_ERROR(decl, "Duplicate name '%s' for macro method.", function->name); + SEMA_PREV(function, "Previous definition here."); + return false; + } + } + DEBUG_LOG("Macro method '%s.%s' analysed.", parent->name, decl->name); + vec_add(parent->methods, decl); + + return true; +} + static inline bool sema_analyse_macro(Context *context, Decl *decl) { TypeInfo *rtype = decl->macro_decl.rtype; - if (decl->macro_decl.rtype && !sema_resolve_type_info(context, rtype)) return false; + if (decl->macro_decl.rtype && !sema_resolve_type_info(context, rtype)) return decl_poison(decl); VECEACH(decl->macro_decl.parameters, i) { Decl *param = decl->macro_decl.parameters[i]; @@ -846,13 +895,17 @@ static inline bool sema_analyse_macro(Context *context, Decl *decl) case VARDECL_PARAM_EXPR: case VARDECL_PARAM_CT: case VARDECL_PARAM_REF: - if (param->var.type_info && !sema_resolve_type_info(context, param->var.type_info)) return false; + if (param->var.type_info) + { + if (!sema_resolve_type_info(context, param->var.type_info)) return decl_poison(decl); + param->type = param->var.type_info->type; + } break; case VARDECL_PARAM_CT_TYPE: if (param->var.type_info) { SEMA_ERROR(param->var.type_info, "A compile time type parameter cannot have a type itself."); - return false; + return decl_poison(decl); } break; case VARDECL_CONST: @@ -874,14 +927,14 @@ static inline bool sema_analyse_macro(Context *context, Decl *decl) switch (param->var.kind) { case VARDECL_PARAM: - if (param->var.type_info && !sema_resolve_type_info(context, param->var.type_info)) return false; + if (param->var.type_info && !sema_resolve_type_info(context, param->var.type_info)) return decl_poison(decl); break; case VARDECL_PARAM_EXPR: case VARDECL_PARAM_CT: case VARDECL_PARAM_REF: case VARDECL_PARAM_CT_TYPE: SEMA_ERROR(param, "Only plain variables are allowed as body parameters."); - break; + return decl_poison(decl); case VARDECL_CONST: case VARDECL_GLOBAL: case VARDECL_LOCAL: @@ -893,8 +946,13 @@ static inline bool sema_analyse_macro(Context *context, Decl *decl) } param->resolve_status = RESOLVE_DONE; } - if (!sema_check_unique_parameters(decl->macro_decl.parameters)) return false; - if (!sema_check_unique_parameters(decl->macro_decl.body_parameters)) return false; + if (!sema_check_unique_parameters(decl->macro_decl.parameters)) return decl_poison(decl); + if (!sema_check_unique_parameters(decl->macro_decl.body_parameters)) return decl_poison(decl); + if (decl->macro_decl.type_parent) + { + if (!sema_analyse_macro_method(context, decl)) return decl_poison(decl); + } + decl->type = type_void; return true; } @@ -921,7 +979,7 @@ static inline bool sema_analyse_global(Context *context, Decl *decl) if (attribute == ATTRIBUTE_NONE) return decl_poison(decl); bool had = false; -#define SET_ATTR(_X) had = decl->func._X; decl->func._X = true; break +#define SET_ATTR(_X) had = decl->func_decl._X; decl->func_decl._X = true; break switch (attribute) { case ATTRIBUTE_CNAME: @@ -1076,7 +1134,7 @@ static Context *copy_context(Module *module, Context *c) copy->imports = copy_decl_list(c->imports); copy->global_decls = copy_decl_list(c->global_decls); copy->module = module; - assert(!c->functions && !c->methods && !c->enums && !c->ct_ifs && !c->types && !c->interfaces && !c->external_symbol_list); + assert(!c->functions && !c->macro_methods && !c->methods && !c->enums && !c->ct_ifs && !c->types && !c->interfaces && !c->external_symbol_list); return copy; } @@ -1169,6 +1227,7 @@ static bool sema_analyse_parameterized_define(Context *c, Decl *decl) instantiated_module = sema_instantiate_module(c, module, path, decl->define_decl.generic_params); sema_analyze_stage(instantiated_module, c->module->stage); } + if (global_context.errors_found) return decl_poison(decl); const char *name_str = TOKSTR(name); Decl *symbol = module_find_symbol(instantiated_module, name_str); assert(symbol); diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 8d7ad8f43..3acf65c01 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -121,7 +121,6 @@ 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; @@ -416,6 +415,8 @@ static inline bool find_possible_inferred_identifier(Type *to, Expr *expr) } +// TODO!!! +/* static inline bool sema_expr_analyse_identifier_resolve(Context *context, Type *to, Expr *expr, ExprIdentifier *id_expr) { Decl *ambiguous_decl = NULL; @@ -484,6 +485,7 @@ static inline bool sema_expr_analyse_identifier_resolve(Context *context, Type * DEBUG_LOG("Resolution successful of %s.", decl->name); return true; } +*/ bool expr_is_constant_eval(Expr *expr) { @@ -550,22 +552,6 @@ static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr SEMA_ERROR(expr, "Functions from other modules, must be prefixed with the module name"); 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; - } - expr->identifier_expr.decl = decl; - expr_set_type(expr, type_void); - return true; - } - if (expr->expr_kind == EXPR_MACRO_IDENTIFIER) - { - SEMA_ERROR(expr, "Only macro expansions can be prefixed with '@', please try removing it.", decl->name); - return false; - } if (decl->resolve_status != RESOLVE_DONE) { if (!sema_analyse_decl(context, decl)) return poisoned_decl; @@ -610,6 +596,33 @@ static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr return true; } +static inline bool sema_expr_analyse_macro_expansion(Context *context, Expr *expr) +{ + Expr *inner = expr->macro_expansion_expr.inner; + if (!sema_analyse_expr_value(context, NULL, inner)) return false; + Decl *decl; + switch (inner->expr_kind) + { + case EXPR_IDENTIFIER: + decl = inner->identifier_expr.decl; + break; + case EXPR_ACCESS: + decl = inner->access_expr.ref; + break; + default: + SEMA_ERROR(expr, "Expected a macro identifier here."); + return false; + } + if (decl->decl_kind != DECL_MACRO) + { + SEMA_ERROR(inner, "Expected a macro identifier here."); + return false; + } + expr->macro_expansion_expr.decl = decl; + expr_copy_properties(expr, inner); + return true; +} + static inline bool sema_expr_analyse_ct_identifier(Context *context, Expr *expr) { expr->pure = true; @@ -745,7 +758,7 @@ static inline bool expr_may_unpack_as_vararg(Expr *expr, Type *variadic_base_typ static inline bool sema_expr_analyse_func_invocation(Context *context, FunctionSignature *signature, Expr *expr, Decl *decl, Type *to, Expr *struct_var) { // 1. Builtin? We handle that elsewhere. - if (decl->func.is_builtin) + if (decl->func_decl.is_builtin) { assert(!struct_var); return sema_expr_analyse_intrinsic_invocation(context, expr, decl, to); @@ -1056,7 +1069,7 @@ static inline Type *unify_returns(Context *context, Type *to) static inline bool sema_expr_analyse_func_call(Context *context, Type *to, Expr *expr, Decl *decl, Expr *struct_var) { expr->call_expr.is_pointer_call = false; - return sema_expr_analyse_func_invocation(context, &decl->func.function_signature, expr, decl, to, struct_var); + return sema_expr_analyse_func_invocation(context, &decl->func_decl.function_signature, expr, decl, to, struct_var); } static bool sema_check_stmt_compile_time(Context *context, Ast *ast); @@ -1102,8 +1115,10 @@ static bool sema_check_stmt_compile_time(Context *context, Ast *ast) } } -static inline bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr *call_expr, Decl *decl) +static inline bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr *call_expr, Expr *struct_var, Decl *decl) { + assert(decl->decl_kind == DECL_MACRO); + // TODO failable if (context->macro_scope.depth >= MAX_MACRO_NESTING) { @@ -1125,15 +1140,27 @@ static inline bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr Expr **args = call_expr->call_expr.arguments; Decl **func_params = decl->macro_decl.parameters; - unsigned num_args = vec_size(args); - if (num_args != vec_size(func_params)) + unsigned explicit_args = vec_size(args); + unsigned total_args = explicit_args; + if (struct_var) + { + total_args++; + vec_add(args, NULL); + for (unsigned i = explicit_args; i > 0; i--) + { + args[i] = args[i - 1]; + } + args[0] = struct_var; + } + + if (total_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++) + Decl **params = func_params > 0 ? VECNEW(Decl *, total_args) : NULL; + for (unsigned i = 0; i < total_args; i++) { Expr *arg = args[i]; Decl *param = copy_decl(func_params[i]); @@ -1287,7 +1314,7 @@ static inline bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr SCOPE_START_WITH_FLAGS(SCOPE_MACRO); - for (unsigned i = 0; i < num_args; i++) + for (unsigned i = 0; i < total_args; i++) { Decl *param = params[i]; sema_add_local(context, param); @@ -1367,7 +1394,7 @@ static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr break; case EXPR_ACCESS: decl = func_expr->access_expr.ref; - if (decl->decl_kind == DECL_FUNC) + if (decl->decl_kind == DECL_FUNC || decl->decl_kind == DECL_MACRO) { expr->call_expr.is_type_method = true; struct_var = expr_new(EXPR_UNARY, func_expr->access_expr.parent->span); @@ -1376,10 +1403,14 @@ static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr struct_var->resolve_status = RESOLVE_DONE; assert(func_expr->access_expr.parent->resolve_status == RESOLVE_DONE); expr_set_type(struct_var, type_get_ptr(struct_var->unary_expr.expr->type)); + if (decl->decl_kind == DECL_MACRO) + { + return sema_expr_analyse_macro_call(context, to, expr, struct_var, decl); + } } break; - case EXPR_MACRO_IDENTIFIER: - return sema_expr_analyse_macro_call(context, to, expr, func_expr->identifier_expr.decl); + case EXPR_MACRO_EXPANSION: + return sema_expr_analyse_macro_call(context, to, expr, NULL, func_expr->macro_expansion_expr.decl); case EXPR_LEN: if (func_expr->type == type_void) { @@ -1410,7 +1441,8 @@ 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: - UNREACHABLE + SEMA_ERROR(expr, "Macro declarations cannot be called without using '@' before the macro name."); + return false; case DECL_GENERIC: return sema_expr_analyse_generic_call(context, to, decl, expr); case DECL_POISONED: @@ -1811,6 +1843,44 @@ static Expr *enum_minmax_value(Decl *decl, BinaryOp comparison) } return expr; } + +/** + * 1. .A -> It is an enum constant. + * 2. .foo -> It is a function. + * 3. .@foo -> It is a macro. + * 4. .#bar -> It is an identifier to resolve as a member or a function + * 5. .@#bar -> It is an identifier to resolve as a macro + */ +static TokenId sema_expr_resolve_access_child(Expr *child) +{ + switch (child->expr_kind) + { + case EXPR_IDENTIFIER: + case EXPR_CONST_IDENTIFIER: + // Not allowed obviously. + if (child->identifier_expr.path) break; + return child->identifier_expr.identifier; + case EXPR_HASH_IDENT: + TODO + case EXPR_MACRO_EXPANSION: + child = child->macro_expansion_expr.inner; + switch (child->expr_kind) + { + case EXPR_IDENTIFIER: + case EXPR_HASH_IDENT: + return sema_expr_resolve_access_child(child); + default: + SEMA_ERROR(child, "Expected a macro name."); + return INVALID_TOKEN_ID; + } + default: + break; + + } + SEMA_ERROR(child, "Expected an identifier here."); + return INVALID_TOKEN_ID; +} + static inline bool sema_expr_analyse_type_access(Expr *expr, TypeInfo *parent, bool was_group) { if (!was_group && type_kind_is_derived(parent->type->type_kind)) @@ -1823,8 +1893,13 @@ static inline bool sema_expr_analyse_type_access(Expr *expr, TypeInfo *parent, b 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); + Expr *child = expr->access_expr.child; + + TokenId identifier_token = sema_expr_resolve_access_child(child); + if (TOKEN_IS_INVALID(identifier_token)) return false; + + TokenType type = TOKTYPE(identifier_token); + const char *name = TOKSTR(identifier_token); if (type == TOKEN_TYPEID) { expr_set_type(expr, type_typeid); @@ -1866,7 +1941,7 @@ static inline bool sema_expr_analyse_type_access(Expr *expr, TypeInfo *parent, b case DECL_ENUM: if (type == TOKEN_CONST_IDENT) { - if (!sema_expr_analyse_enum_constant(expr, expr->access_expr.sub_element, decl)) + if (!sema_expr_analyse_enum_constant(expr, identifier_token, decl)) { SEMA_ERROR(expr, "'%s' has no enumeration value '%s'.", decl->name, name); return false; @@ -1942,13 +2017,18 @@ static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr) expr->constant = true; expr->pure = true; - TokenType type = TOKTYPE(expr->access_expr.sub_element); - const char *name = TOKSTR(expr->access_expr.sub_element); + Expr *child = expr->access_expr.child; + + TokenId identifier_token = sema_expr_resolve_access_child(child); + if (TOKEN_IS_INVALID(identifier_token)) return false; + TokenType type = TOKTYPE(identifier_token); + const char *name = TOKSTR(identifier_token); Decl *ref = parent->access_expr.ref; + bool is_macro = child->expr_kind == EXPR_MACRO_EXPANSION; bool is_plain_member = ref->decl_kind == DECL_VAR; - if (type == TOKEN_TYPEID) + if (type == TOKEN_TYPEID && !is_macro) { expr_set_type(expr, type_typeid); expr->expr_kind = EXPR_TYPEID; @@ -1963,17 +2043,17 @@ static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr) expr->resolve_status = RESOLVE_DONE; return true; } - if (name == kw_sizeof) + if (name == kw_sizeof && !is_macro) { expr_rewrite_to_int_const(expr, type_usize, type_size(ref->type)); return true; } - if (name == kw_alignof) + if (name == kw_alignof && !is_macro) { expr_rewrite_to_int_const(expr, type_usize, type_abi_alignment(ref->type)); return true; } - if (name == kw_ordinal) + if (name == kw_ordinal && !is_macro) { if (ref->decl_kind == DECL_ENUM_CONSTANT) { @@ -1981,16 +2061,16 @@ static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr) return true; } } - if (name == kw_nameof) + if (name == kw_nameof && !is_macro) { expr_rewrite_to_string(expr, ref->name); return true; } - if (name == kw_qnameof) + if (name == kw_qnameof && !is_macro) { TODO } - if (name == kw_kindof) + if (name == kw_kindof && !is_macro) { TODO } @@ -2001,7 +2081,12 @@ static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr) { 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); + if (is_macro) + { + SEMA_ERROR(expr, "'%s' does not have a macro '%s'.", type_to_error_string(ref->type), name); + return false; + } + 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. @@ -2009,11 +2094,24 @@ static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr) } VECEACH(ref->methods, i) { - Decl *function = ref->methods[i]; - if (name == function->name) + Decl *decl = ref->methods[i]; + bool is_macro_decl = decl->decl_kind == DECL_MACRO; + if (name == decl->name) { - expr->access_expr.ref = function; - expr_set_type(expr, function->type); + if (is_macro != is_macro_decl) + { + if (is_macro) + { + SEMA_ERROR(child, "Method '%s' should not be prefixed with '@'.", name); + } + else + { + SEMA_ERROR(child, "Macro method '%s' must be prefixed with '@'.", name); + } + return false; + } + expr->access_expr.ref = decl; + expr_set_type(expr, decl->type); return true; } } @@ -2022,6 +2120,11 @@ static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr) Decl *member = ref->strukt.members[i]; if (name == member->name) { + if (is_macro) + { + SEMA_ERROR(child, "member '%s' should not be prefixed with '@'.", name); + return false; + } expr->expr_kind = EXPR_MEMBER_ACCESS; expr->access_expr.ref = member; expr_set_type(expr, member->type); @@ -2048,6 +2151,27 @@ static inline bool sema_expr_analyse_access(Context *context, Expr *expr) { return sema_expr_analyse_member_access(context, expr); } + bool is_function_or_macro = false; + if (parent->expr_kind == EXPR_ACCESS) + { + is_function_or_macro = parent->access_expr.ref->decl_kind == DECL_FUNC || parent->access_expr.ref->decl_kind == DECL_MACRO; + } + if (parent->expr_kind == EXPR_IDENTIFIER) + { + is_function_or_macro = parent->identifier_expr.decl->decl_kind == DECL_FUNC || parent->identifier_expr.decl->decl_kind == DECL_MACRO; + } + + if (is_function_or_macro) + { + SEMA_ERROR(expr->access_expr.child, "No such member or function could be found."); + return false; + } + Expr *child = expr->access_expr.child; + bool is_macro = child->expr_kind == EXPR_MACRO_EXPANSION; + + TokenId identifier_token = sema_expr_resolve_access_child(child); + if (TOKEN_IS_INVALID(identifier_token)) return false; + expr->failable = parent->failable; assert(expr->expr_kind == EXPR_ACCESS); @@ -2064,13 +2188,14 @@ static inline bool sema_expr_analyse_access(Context *context, Expr *expr) insert_access_deref(expr); parent = expr->access_expr.parent; } - const char *kw = TOKSTR(expr->access_expr.sub_element); + const char *kw = TOKSTR(identifier_token); Expr *current_parent = parent; CHECK_DEEPER: switch (type->type_kind) { case TYPE_SUBARRAY: + if (is_macro) goto NO_MATCH; if (kw == kw_sizeof) { expr_rewrite_to_int_const(expr, type_usize, type_size(type)); @@ -2086,6 +2211,7 @@ CHECK_DEEPER: } goto NO_MATCH; case TYPE_ARRAY: + if (is_macro) goto NO_MATCH; if (kw == kw_sizeof) { expr_rewrite_to_int_const(expr, type_usize, type_size(type)); @@ -2104,7 +2230,7 @@ CHECK_DEEPER: break; default: NO_MATCH: - SEMA_ERROR(expr, "There is no member or method '%s' on '%s'", TOKSTR(expr->access_expr.sub_element), type_to_error_string(parent_type)); + SEMA_ERROR(expr, "There is no member or method '%s' on '%s'", TOKSTR(identifier_token), type_to_error_string(parent_type)); return false; } @@ -2129,6 +2255,16 @@ CHECK_DEEPER: SEMA_ERROR(expr, "There is no field or method '%s.%s'.", decl->name, kw); return false; } + if (member->decl_kind == DECL_MACRO && !is_macro) + { + SEMA_ERROR(expr, "Expected '@' before the macro name."); + return false; + } + if (member->decl_kind != DECL_MACRO && is_macro) + { + SEMA_ERROR(expr, "'@' should only be placed in front of macro names."); + return false; + } expr->access_expr.parent = current_parent; expr->constant = expr->access_expr.parent->constant; expr->pure = expr->access_expr.parent->pure; @@ -3971,7 +4107,6 @@ static bool sema_expr_analyse_comp(Context *context, Expr *expr, Expr *left, Exp return false; case TYPE_VOID: case TYPE_TYPEINFO: - case TYPE_MEMBER: case TYPE_TYPEDEF: case TYPE_DISTINCT: case TYPE_INFERRED_ARRAY: @@ -4122,10 +4257,9 @@ static bool sema_take_addr_of(Expr *inner, bool *is_constant) 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: + case EXPR_MACRO_EXPANSION: SEMA_ERROR(inner, "It's not possible to take the address of a macro."); return false; case EXPR_IDENTIFIER: @@ -4336,7 +4470,6 @@ static bool sema_expr_analyse_not(Expr *expr, Expr *inner) 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; } @@ -4819,7 +4952,6 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr * UNREACHABLE case EXPR_HASH_IDENT: return sema_expr_analyse_hash_identifier(context, to, expr); - case EXPR_MACRO_CT_IDENTIFIER: case EXPR_CT_IDENT: return sema_expr_analyse_ct_identifier(context, expr); case EXPR_FAILABLE: @@ -4874,9 +5006,10 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr * return true; case EXPR_TYPEID: return sema_expr_analyse_type(context, expr); + case EXPR_MACRO_EXPANSION: + return sema_expr_analyse_macro_expansion(context, 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); @@ -4924,6 +5057,18 @@ static inline bool sema_cast_rvalue(Context *context, Type *to, Expr *expr) if (!expr_ok(expr)) return false; switch (expr->expr_kind) { + case EXPR_ACCESS: + if (expr->access_expr.ref->decl_kind == DECL_FUNC) + { + SEMA_ERROR(expr, "A function name must be followed by '(' or preceeded by '&'."); + return false; + } + if (expr->access_expr.ref->decl_kind == DECL_MACRO) + { + SEMA_ERROR(expr, "A macro name must be followed by '('."); + return false; + } + break; case EXPR_MEMBER_ACCESS: if (expr->access_expr.ref->decl_kind == DECL_ENUM_CONSTANT) { @@ -4946,11 +5091,8 @@ static inline bool sema_cast_rvalue(Context *context, Type *to, Expr *expr) 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); + case EXPR_MACRO_EXPANSION: + SEMA_ERROR(expr, "Expected macro followed by (...).", expr->ct_macro_ident_expr.identifier); return expr_poison(expr); case EXPR_CT_IDENT: if (!sema_cast_ct_ident_rvalue(context, to, expr)) return false; diff --git a/src/compiler/sema_name_resolution.c b/src/compiler/sema_name_resolution.c index 8d40e0d46..03aebee95 100644 --- a/src/compiler/sema_name_resolution.c +++ b/src/compiler/sema_name_resolution.c @@ -373,7 +373,7 @@ bool sema_add_local(Context *context, Decl *decl) decl_poison(other); return false; } - Decl ***vars = &context->active_function_for_analysis->func.annotations->vars; + Decl ***vars = &context->active_function_for_analysis->func_decl.annotations->vars; unsigned num_vars = vec_size(*vars); if (num_vars == MAX_LOCALS - 1) { diff --git a/src/compiler/sema_passes.c b/src/compiler/sema_passes.c index c6db39b34..c8372ddc0 100644 --- a/src/compiler/sema_passes.c +++ b/src/compiler/sema_passes.c @@ -10,7 +10,7 @@ void context_add_intrinsic(Context *context, const char *name) decl->module = context->module; decl->decl_kind = DECL_FUNC; decl->resolve_status = RESOLVE_DONE; - decl->func.is_builtin = true; + decl->func_decl.is_builtin = true; decl->name = name; Decl *old = stable_set(&context->local_symbols, decl->name, decl); assert(!old); @@ -188,7 +188,7 @@ void sema_analysis_pass_ct_assert(Module *module) static inline bool analyse_func_body(Context *context, Decl *decl) { - if (!decl->func.body) return true; + if (!decl->func_decl.body) return true; if (!sema_analyse_function_body(context, decl)) return decl_poison(decl); return true; } @@ -228,6 +228,10 @@ void sema_analysis_pass_decls(Module *module) { sema_analyse_decl(context, context->methods[i]); } + VECEACH(context->macro_methods, i) + { + sema_analyse_decl(context, context->macro_methods[i]); + } VECEACH(context->vars, i) { sema_analyse_decl(context, context->vars[i]); diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index bf379a77c..acfce1f66 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -1853,7 +1853,7 @@ bool sema_analyse_statement(Context *context, Ast *statement) bool sema_analyse_function_body(Context *context, Decl *func) { if (!decl_ok(func)) return false; - FunctionSignature *signature = &func->func.function_signature; + FunctionSignature *signature = &func->func_decl.function_signature; context->active_function_for_analysis = func; context->rtype = signature->rtype->type; context->active_scope = (DynamicScope) { @@ -1874,7 +1874,7 @@ bool sema_analyse_function_body(Context *context, Decl *func) context->next_target = 0; context->next_switch = 0; context->break_target = 0; - func->func.annotations = CALLOCS(FuncAnnotations); + func->func_decl.annotations = CALLOCS(FuncAnnotations); SCOPE_START assert(context->active_scope.depth == 1); Decl **params = signature->params; @@ -1882,7 +1882,7 @@ bool sema_analyse_function_body(Context *context, Decl *func) { if (!sema_add_local(context, params[i])) return false; } - if (!sema_analyse_compound_statement_no_scope(context, func->func.body)) return false; + if (!sema_analyse_compound_statement_no_scope(context, func->func_decl.body)) return false; assert(context->active_scope.depth == 1); if (!context->active_scope.jump_end) { diff --git a/src/compiler/types.c b/src/compiler/types.c index 89a5d78d8..33a9481d1 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -130,8 +130,6 @@ const char *type_to_error_string(Type *type) case TYPE_VECTOR: asprintf(&buffer, "%s[<%llu>]", type_to_error_string(type->array.base), (unsigned long long)type->array.len); return buffer; - case TYPE_MEMBER: - return "member"; case TYPE_TYPEINFO: return "typeinfo"; case TYPE_TYPEID: @@ -196,7 +194,6 @@ ByteSize type_size(Type *type) return type_size(type->vector.base) * type->vector.len; case TYPE_POISONED: case TYPE_TYPEINFO: - case TYPE_MEMBER: case TYPE_INFERRED_ARRAY: UNREACHABLE; case TYPE_TYPEDEF: @@ -355,7 +352,6 @@ bool type_is_abi_aggregate(Type *type) case TYPE_VIRTUAL_ANY: return true; case TYPE_TYPEINFO: - case TYPE_MEMBER: case TYPE_INFERRED_ARRAY: UNREACHABLE } @@ -495,7 +491,6 @@ bool type_is_homogenous_aggregate(Type *type, Type **base, unsigned *elements) case TYPE_IXX: case TYPE_VOID: case TYPE_TYPEINFO: - case TYPE_MEMBER: case TYPE_TYPEID: case TYPE_FUNC: case TYPE_STRLIT: @@ -647,7 +642,6 @@ AlignSize type_abi_alignment(Type *type) { case TYPE_POISONED: case TYPE_TYPEINFO: - case TYPE_MEMBER: case TYPE_INFERRED_ARRAY: UNREACHABLE; case TYPE_VECTOR: @@ -1058,7 +1052,6 @@ static void type_append_name_to_scratch(Type *type) case TYPE_STRLIT: case TYPE_INFERRED_ARRAY: case TYPE_TYPEINFO: - case TYPE_MEMBER: UNREACHABLE break; case TYPE_FUNC: @@ -1176,7 +1169,6 @@ bool type_is_scalar(Type *type) { case TYPE_POISONED: case TYPE_TYPEINFO: - case TYPE_MEMBER: case TYPE_INFERRED_ARRAY: UNREACHABLE case TYPE_VOID: @@ -1377,7 +1369,6 @@ Type *type_find_max_type(Type *type, Type *other) case TYPE_VOID: case TYPE_BOOL: case TYPE_TYPEINFO: - case TYPE_MEMBER: case TYPE_VIRTUAL: case TYPE_VIRTUAL_ANY: return NULL; diff --git a/src/version.h b/src/version.h index c65ec02d8..fd6f811ef 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "A228" \ No newline at end of file +#define COMPILER_VERSION "A229" \ No newline at end of file diff --git a/test/test_suite/macro_methods/access.c3 b/test/test_suite/macro_methods/access.c3 new file mode 100644 index 000000000..2221e3b2b --- /dev/null +++ b/test/test_suite/macro_methods/access.c3 @@ -0,0 +1,34 @@ +struct An1 +{ + An3 x; +} + +struct An3 +{ + An2 y; +} + +struct An2 +{ +} + +extern func void printf(char *string); + +macro void An2.helloWorld(An2 *an2) +{ + printf("An2 hello\n"); +} + +func void check() +{ + printf("Checking\n"); +} + + +func void main() +{ + An1 an; + an.x.y.@helloWorld(); + An2 a; + a.@helloWorld(); +} \ No newline at end of file diff --git a/test/test_suite/macro_methods/macro_method_fails.c3 b/test/test_suite/macro_methods/macro_method_fails.c3 new file mode 100644 index 000000000..250cab8ba --- /dev/null +++ b/test/test_suite/macro_methods/macro_method_fails.c3 @@ -0,0 +1,56 @@ +struct An1 +{ + An3 x; +} + +struct An3 +{ + An2 y; +} + +struct An2 +{ +} + +extern func void printf(char *string); + +macro void An2.helloWorld(An2 *an2) +{ + printf("An2 hello\n"); +} + +func void check() +{ + printf("Checking\n"); +} + + +func void test1() +{ + An1 an; + an.x.y.@helloWorld; // #error: macro name must be followed by '(' +} + +func void test2() +{ + An2 a; + a.@helloWorld; // #error: A macro name must be followed by '(' +} + +func void test3() +{ + An2 a; + a.@helloWorld.b; // #error: No such member or function could be found. +} + +func void test4() +{ + An1 an; + an.x.@y; // #error: '@' should only be placed in front of macro names +} + +func void test5() +{ + An1 an; + an.x.@y(); // #error: '@' should only be placed in front of macro names +} \ No newline at end of file diff --git a/test/test_suite/methods/access.c3 b/test/test_suite/methods/access.c3 new file mode 100644 index 000000000..d3f15df2e --- /dev/null +++ b/test/test_suite/methods/access.c3 @@ -0,0 +1,37 @@ +extern func void printf(...); + +struct An1 +{ + An3 x; +} + +struct An3 +{ + An2 y; +} + +define AnCall = func void(); + +struct An2 +{ + AnCall t; +} + +func void An2.helloWorld(An2 *an2) +{ + printf("An2 hello\n"); +} + +func void check() +{ + printf("Checking\n"); +} + + +func void main() +{ + An1 an; + an.x.y.helloWorld(); + an.x.y.t = ✓ + an.x.y.t(); +} \ No newline at end of file