From b2be8349ed94b60b114b80786109ad99d34b6b85 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sun, 23 Jan 2022 19:43:57 +0100 Subject: [PATCH] Add typeless varargs. --- src/compiler/compiler_internal.h | 2 + src/compiler/llvm_codegen_expr.c | 4 +- src/compiler/parse_global.c | 23 +++- src/compiler/sema_expr.c | 13 ++- src/version.h | 2 +- .../test_suite/functions/typeless_varargs.c3t | 109 ++++++++++++++++++ 6 files changed, 144 insertions(+), 9 deletions(-) create mode 100644 test/test_suite/functions/typeless_varargs.c3t diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 41dc23462..ce0b43641 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -350,6 +350,7 @@ typedef struct VarDecl_ bool unwrap : 1; bool shadow : 1; bool vararg : 1; + bool vararg_implicit : 1; bool is_static : 1; bool is_read : 1; bool is_written : 1; @@ -433,6 +434,7 @@ typedef enum { VARIADIC_NONE, VARIADIC_TYPED, + VARIADIC_ANY, VARIADIC_RAW, } Variadic; diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 96707dd11..6cd35ffaa 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -4509,7 +4509,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) Expr **args = expr->call_expr.arguments; unsigned arguments = vec_size(args); - if (prototype->variadic == VARIADIC_TYPED) non_variadic_params--; + if (prototype->variadic == VARIADIC_TYPED || prototype->variadic == VARIADIC_ANY) non_variadic_params--; FunctionPrototype copy; if (prototype->variadic == VARIADIC_RAW) { @@ -4600,7 +4600,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) } // 9. Typed varargs - if (prototype->variadic == VARIADIC_TYPED) + if (prototype->variadic == VARIADIC_TYPED || prototype->variadic == VARIADIC_ANY) { REMINDER("All varargs should be called with non-alias!"); Type *vararg_param = params[non_variadic_params]; diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 7a7e04fc6..a6b8a9556 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -1059,12 +1059,30 @@ bool parse_parameters(ParseContext *context, Visibility visibility, Decl ***para VarDeclKind param_kind; TokenId token = context->tok.id; bool no_name = false; - + bool vararg_implicit = false; switch (context->tok.type) { case TOKEN_IDENT: // normal foo param_kind = VARDECL_PARAM; + // Check for "foo..." which defines an implicit "any" vararg + if (context->next_tok.type == TOKEN_ELLIPSIS) + { + if (ellipsis) + { + SEMA_TOKEN_ERROR(context->tok, "Unexpected '...' here."); + return false; + } + advance(context); + if (type) + { + SEMA_TOKEN_ERROR(context->tok, "The '...' should appear after the type."); + return false; + } + type = type_info_new_base(type_any, source_span_from_token_id(token)); + ellipsis = true; + vararg_implicit = true; + } break; case TOKEN_CT_IDENT: // ct_var $foo @@ -1126,6 +1144,7 @@ bool parse_parameters(ParseContext *context, Visibility visibility, Decl ***para if (!parse_attributes(context, ¶m->attributes)) return false; var_arg_found |= ellipsis; param->var.vararg = ellipsis; + param->var.vararg_implicit = vararg_implicit; vec_add(params, param); if (!try_consume(context, TOKEN_COMMA)) break; } @@ -1155,7 +1174,7 @@ static inline bool parse_parameter_list(ParseContext *context, Visibility parent } else { - signature->variadic = VARIADIC_TYPED; + signature->variadic = last->var.vararg_implicit ? VARIADIC_ANY : VARIADIC_TYPED; } } } diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 9905ccf37..2997aa747 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1235,7 +1235,7 @@ static inline bool sema_check_invalid_body_arguments(SemaContext *context, Expr } -static inline bool sema_expand_call_arguments(SemaContext *context, CalledDecl *callee, Expr *call, Expr **args, unsigned func_param_count, bool variadic, bool *failable) +static inline bool sema_expand_call_arguments(SemaContext *context, CalledDecl *callee, Expr *call, Expr **args, unsigned func_param_count, Variadic variadic, bool *failable) { unsigned num_args = vec_size(args); Decl **params = callee->params; @@ -1304,7 +1304,7 @@ static inline bool sema_expand_call_arguments(SemaContext *context, CalledDecl * if (i >= func_param_count) { // 11. We might have a typed variadic argument. - if (!variadic) + if (variadic == VARIADIC_NONE) { // 15. We have too many parameters... SEMA_ERROR(arg, "This argument would would exceed the number of parameters, did you add too many arguments?"); @@ -1322,6 +1322,11 @@ static inline bool sema_expand_call_arguments(SemaContext *context, CalledDecl * return false; } } + else if (variadic == VARIADIC_ANY) + { + if (!sema_analyse_expr(context, arg)) return false; + expr_insert_addr(arg); + } } actual_args[i] = arg; } @@ -1416,7 +1421,7 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr // 6. We might have a typed variadic call e.g. foo(int, double...) // get that type. Type *variadic_type = NULL; - if (callee.variadic == VARIADIC_TYPED) + if (callee.variadic == VARIADIC_TYPED || callee.variadic == VARIADIC_ANY) { // 7a. The parameter type is [], so we get the Type *last_type = callee.macro ? callee.params[func_param_count - 1]->type : callee.param_types[func_param_count - 1]; @@ -1426,7 +1431,7 @@ static inline bool sema_expr_analyse_call_invocation(SemaContext *context, Expr func_param_count--; } - if (!sema_expand_call_arguments(context, &callee, call, args, func_param_count, callee.variadic != VARIADIC_NONE, failable)) return false; + if (!sema_expand_call_arguments(context, &callee, call, args, func_param_count, callee.variadic, failable)) return false; args = call->call_expr.arguments; num_args = vec_size(args); diff --git a/src/version.h b/src/version.h index 7b8e9a843..aad120eb5 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "PRE.17" \ No newline at end of file +#define COMPILER_VERSION "PRE.18" \ No newline at end of file diff --git a/test/test_suite/functions/typeless_varargs.c3t b/test/test_suite/functions/typeless_varargs.c3t new file mode 100644 index 000000000..a9e3074d2 --- /dev/null +++ b/test/test_suite/functions/typeless_varargs.c3t @@ -0,0 +1,109 @@ +// #target: x64-darwin + +module test; + +extern fn void printf(char*, ...); + +fn void retest(foo...) +{ + test(...foo); +} +fn void test(foo...) +{ + printf("Foo1 was: %d\n", *((int*)foo[0])); +} +fn int main() +{ + int i = 1; + test(1); + retest(1); + return 1; +} + +/* #expect: test.ll + +define void @test.retest(i8* %0, i64 %1) #0 { +entry: + %foo = alloca %"variant[]", align 8 + %vararg = alloca %"variant[]", align 8 + %pair = bitcast %"variant[]"* %foo to { i8*, i64 }* + %2 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %pair, i32 0, i32 0 + store i8* %0, i8** %2, align 8 + %3 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %pair, i32 0, i32 1 + store i64 %1, i64* %3, align 8 + %4 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg, i32 0, i32 1 + %5 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg, i32 0, i32 0 + %6 = bitcast %"variant[]"* %foo to { i8*, i64 }* + %7 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %6, i32 0, i32 0 + %lo = load i8*, i8** %7, align 8 + %8 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %6, i32 0, i32 1 + %hi = load i64, i64* %8, align 8 + call void @test.test(i8* %lo, i64 %hi) + ret void +} + +define void @test.test(i8* %0, i64 %1) #0 { +entry: + %foo = alloca %"variant[]", align 8 + %pair = bitcast %"variant[]"* %foo to { i8*, i64 }* + %2 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %pair, i32 0, i32 0 + store i8* %0, i8** %2, align 8 + %3 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %pair, i32 0, i32 1 + store i64 %1, i64* %3, align 8 + %4 = getelementptr inbounds %"variant[]", %"variant[]"* %foo, i32 0, i32 0 + %5 = load %variant*, %variant** %4, align 8 + %ptroffset = getelementptr inbounds %variant, %variant* %5, i64 0 + %6 = getelementptr inbounds %variant, %variant* %ptroffset, i32 0, i32 0 + %7 = bitcast i8** %6 to i32** + %8 = load i32*, i32** %7, align 8 + %9 = load i32, i32* %8, align 8 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @.str, i32 0, i32 0), i32 %9) + ret void +} + +define i32 @main() #0 { +entry: + %i = alloca i32, align 4 + %vararg = alloca %"variant[]", align 8 + %varargslots = alloca [1 x %variant], align 16 + %taddr = alloca i32, align 4 + %vararg1 = alloca %"variant[]", align 8 + %varargslots2 = alloca [1 x %variant], align 16 + %taddr3 = alloca i32, align 4 + store i32 1, i32* %i, align 4 + store i32 1, i32* %taddr, align 4 + %0 = bitcast i32* %taddr to i8* + %1 = insertvalue %variant undef, i8* %0, 0 + %2 = insertvalue %variant %1, i64 5, 1 + %3 = getelementptr inbounds [1 x %variant], [1 x %variant]* %varargslots, i64 0, i64 0 + store %variant %2, %variant* %3, align 16 + %4 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg, i32 0, i32 1 + store i64 1, i64* %4, align 8 + %5 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg, i32 0, i32 0 + %6 = bitcast [1 x %variant]* %varargslots to %variant* + store %variant* %6, %variant** %5, align 8 + %7 = bitcast %"variant[]"* %vararg to { i8*, i64 }* + %8 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %7, i32 0, i32 0 + %lo = load i8*, i8** %8, align 8 + %9 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %7, i32 0, i32 1 + %hi = load i64, i64* %9, align 8 + call void @test.test(i8* %lo, i64 %hi) + store i32 1, i32* %taddr3, align 4 + %10 = bitcast i32* %taddr3 to i8* + %11 = insertvalue %variant undef, i8* %10, 0 + %12 = insertvalue %variant %11, i64 5, 1 + %13 = getelementptr inbounds [1 x %variant], [1 x %variant]* %varargslots2, i64 0, i64 0 + store %variant %12, %variant* %13, align 16 + %14 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg1, i32 0, i32 1 + store i64 1, i64* %14, align 8 + %15 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg1, i32 0, i32 0 + %16 = bitcast [1 x %variant]* %varargslots2 to %variant* + store %variant* %16, %variant** %15, align 8 + %17 = bitcast %"variant[]"* %vararg1 to { i8*, i64 }* + %18 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %17, i32 0, i32 0 + %lo4 = load i8*, i8** %18, align 8 + %19 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %17, i32 0, i32 1 + %hi5 = load i64, i64* %19, align 8 + call void @test.retest(i8* %lo4, i64 %hi5) + ret i32 1 +} \ No newline at end of file