| author | |
| committer | |
| log | c42c91ee7c630d47e6adc0a940b5f10bbe04d13a |
| tree | e953816629530d2d532c097cbac3bb846e51763f |
| parent | fcdd808c5c1b866c2582a17839a53ce7bbbb78d6 |
closes #3775 files changed, 67 insertions(+), 21 deletions(-)
src/analyze.cpp+12-9| ... | @@ -1033,10 +1033,10 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c | ... | @@ -1033,10 +1033,10 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c |
| 1033 | AstNode *param_node = fn_proto->params.at(fn_type_id.next_param_index); | 1033 | AstNode *param_node = fn_proto->params.at(fn_type_id.next_param_index); |
| 1034 | assert(param_node->type == NodeTypeParamDecl); | 1034 | assert(param_node->type == NodeTypeParamDecl); |
| 1035 | 1035 | ||
| 1036 | bool param_is_inline = param_node->data.param_decl.is_inline; | 1036 | bool param_is_comptime = param_node->data.param_decl.is_inline; |
| 1037 | bool param_is_var_args = param_node->data.param_decl.is_var_args; | 1037 | bool param_is_var_args = param_node->data.param_decl.is_var_args; |
| 1038 | 1038 | ||
| 1039 | if (param_is_inline) { | 1039 | if (param_is_comptime) { |
| 1040 | if (fn_type_id.is_extern) { | 1040 | if (fn_type_id.is_extern) { |
| 1041 | add_node_error(g, param_node, | 1041 | add_node_error(g, param_node, |
| 1042 | buf_sprintf("comptime parameter not allowed in extern function")); | 1042 | buf_sprintf("comptime parameter not allowed in extern function")); |
| ... | @@ -2507,7 +2507,10 @@ bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry * | ... | @@ -2507,7 +2507,10 @@ bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry * |
| 2507 | if (expected_type->data.fn.fn_type_id.is_var_args != actual_type->data.fn.fn_type_id.is_var_args) { | 2507 | if (expected_type->data.fn.fn_type_id.is_var_args != actual_type->data.fn.fn_type_id.is_var_args) { |
| 2508 | return false; | 2508 | return false; |
| 2509 | } | 2509 | } |
| 2510 | if (!expected_type->data.fn.fn_type_id.is_var_args && | 2510 | if (expected_type->data.fn.is_generic != actual_type->data.fn.is_generic) { |
| 2511 | return false; | ||
| 2512 | } | ||
| 2513 | if (!expected_type->data.fn.is_generic && | ||
| 2511 | actual_type->data.fn.fn_type_id.return_type->id != TypeTableEntryIdUnreachable && | 2514 | actual_type->data.fn.fn_type_id.return_type->id != TypeTableEntryIdUnreachable && |
| 2512 | !types_match_const_cast_only( | 2515 | !types_match_const_cast_only( |
| 2513 | expected_type->data.fn.fn_type_id.return_type, | 2516 | expected_type->data.fn.fn_type_id.return_type, |
| ... | @@ -2518,12 +2521,12 @@ bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry * | ... | @@ -2518,12 +2521,12 @@ bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry * |
| 2518 | if (expected_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) { | 2521 | if (expected_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) { |
| 2519 | return false; | 2522 | return false; |
| 2520 | } | 2523 | } |
| 2521 | for (size_t i = 0; i < expected_type->data.fn.fn_type_id.param_count; i += 1) { | 2524 | if (expected_type->data.fn.fn_type_id.next_param_index != actual_type->data.fn.fn_type_id.next_param_index) { |
| 2522 | if (i == expected_type->data.fn.fn_type_id.param_count - 1 && | 2525 | return false; |
| 2523 | expected_type->data.fn.fn_type_id.is_var_args) | 2526 | } |
| 2524 | { | 2527 | assert(expected_type->data.fn.is_generic || |
| 2525 | continue; | 2528 | expected_type->data.fn.fn_type_id.next_param_index == expected_type->data.fn.fn_type_id.param_count); |
| 2526 | } | 2529 | for (size_t i = 0; i < expected_type->data.fn.fn_type_id.next_param_index; i += 1) { |
| 2527 | // note it's reversed for parameters | 2530 | // note it's reversed for parameters |
| 2528 | FnTypeParamInfo *actual_param_info = &actual_type->data.fn.fn_type_id.param_info[i]; | 2531 | FnTypeParamInfo *actual_param_info = &actual_type->data.fn.fn_type_id.param_info[i]; |
| 2529 | FnTypeParamInfo *expected_param_info = &expected_type->data.fn.fn_type_id.param_info[i]; | 2532 | FnTypeParamInfo *expected_param_info = &expected_type->data.fn.fn_type_id.param_info[i]; |
src/ir.cpp+8| ... | @@ -13089,12 +13089,20 @@ static TypeTableEntry *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruc | ... | @@ -13089,12 +13089,20 @@ static TypeTableEntry *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruc |
| 13089 | } | 13089 | } |
| 13090 | } | 13090 | } |
| 13091 | IrInstruction *param_type_value = instruction->param_types[fn_type_id.next_param_index]->other; | 13091 | IrInstruction *param_type_value = instruction->param_types[fn_type_id.next_param_index]->other; |
| 13092 | if (type_is_invalid(param_type_value->value.type)) | ||
| 13093 | return ira->codegen->builtin_types.entry_invalid; | ||
| 13092 | 13094 | ||
| 13093 | FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index]; | 13095 | FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index]; |
| 13094 | param_info->is_noalias = param_node->data.param_decl.is_noalias; | 13096 | param_info->is_noalias = param_node->data.param_decl.is_noalias; |
| 13095 | param_info->type = ir_resolve_type(ira, param_type_value); | 13097 | param_info->type = ir_resolve_type(ira, param_type_value); |
| 13096 | if (type_is_invalid(param_info->type)) | 13098 | if (type_is_invalid(param_info->type)) |
| 13097 | return ira->codegen->builtin_types.entry_invalid; | 13099 | return ira->codegen->builtin_types.entry_invalid; |
| 13100 | |||
| 13101 | if (param_info->type->id == TypeTableEntryIdVar) { | ||
| 13102 | ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); | ||
| 13103 | out_val->data.x_type = get_generic_fn_type(ira->codegen, &fn_type_id); | ||
| 13104 | return ira->codegen->builtin_types.entry_type; | ||
| 13105 | } | ||
| 13098 | } | 13106 | } |
| 13099 | 13107 | ||
| 13100 | IrInstruction *return_type_value = instruction->return_type->other; | 13108 | IrInstruction *return_type_value = instruction->return_type->other; |
test/cases/generics.zig+24-12| ... | @@ -1,6 +1,6 @@ | ... | @@ -1,6 +1,6 @@ |
| 1 | const assert = @import("std").debug.assert; | 1 | const assert = @import("std").debug.assert; |
| 2 | 2 | ||
| 3 | test "simpleGenericFn" { | 3 | test "simple generic fn" { |
| 4 | assert(max(i32, 3, -1) == 3); | 4 | assert(max(i32, 3, -1) == 3); |
| 5 | assert(max(f32, 0.123, 0.456) == 0.456); | 5 | assert(max(f32, 0.123, 0.456) == 0.456); |
| 6 | assert(add(2, 3) == 5); | 6 | assert(add(2, 3) == 5); |
| ... | @@ -15,7 +15,7 @@ fn add(comptime a: i32, b: i32) -> i32 { | ... | @@ -15,7 +15,7 @@ fn add(comptime a: i32, b: i32) -> i32 { |
| 15 | } | 15 | } |
| 16 | 16 | ||
| 17 | const the_max = max(u32, 1234, 5678); | 17 | const the_max = max(u32, 1234, 5678); |
| 18 | test "compileTimeGenericEval" { | 18 | test "compile time generic eval" { |
| 19 | assert(the_max == 5678); | 19 | assert(the_max == 5678); |
| 20 | } | 20 | } |
| 21 | 21 | ||
| ... | @@ -31,21 +31,22 @@ fn sameButWithFloats(a: f64, b: f64) -> f64 { | ... | @@ -31,21 +31,22 @@ fn sameButWithFloats(a: f64, b: f64) -> f64 { |
| 31 | max(f64, a, b) | 31 | max(f64, a, b) |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | test "fnWithInlineArgs" { | 34 | test "fn with comptime args" { |
| 35 | assert(gimmeTheBigOne(1234, 5678) == 5678); | 35 | assert(gimmeTheBigOne(1234, 5678) == 5678); |
| 36 | assert(shouldCallSameInstance(34, 12) == 34); | 36 | assert(shouldCallSameInstance(34, 12) == 34); |
| 37 | assert(sameButWithFloats(0.43, 0.49) == 0.49); | 37 | assert(sameButWithFloats(0.43, 0.49) == 0.49); |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | 40 | ||
| 41 | test "varParams" { | 41 | test "var params" { |
| 42 | assert(max_i32(12, 34) == 34); | 42 | assert(max_i32(12, 34) == 34); |
| 43 | assert(max_f64(1.2, 3.4) == 3.4); | 43 | assert(max_f64(1.2, 3.4) == 3.4); |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | // TODO `_` | 46 | comptime { |
| 47 | const _1 = assert(max_i32(12, 34) == 34); | 47 | assert(max_i32(12, 34) == 34); |
| 48 | const _2 = assert(max_f64(1.2, 3.4) == 3.4); | 48 | assert(max_f64(1.2, 3.4) == 3.4); |
| 49 | } | ||
| 49 | 50 | ||
| 50 | fn max_var(a: var, b: var) -> @typeOf(a + b) { | 51 | fn max_var(a: var, b: var) -> @typeOf(a + b) { |
| 51 | if (a > b) a else b | 52 | if (a > b) a else b |
| ... | @@ -72,7 +73,7 @@ pub fn SmallList(comptime T: type, comptime STATIC_SIZE: usize) -> type { | ... | @@ -72,7 +73,7 @@ pub fn SmallList(comptime T: type, comptime STATIC_SIZE: usize) -> type { |
| 72 | } | 73 | } |
| 73 | } | 74 | } |
| 74 | 75 | ||
| 75 | test "functionWithReturnTypeType" { | 76 | test "function with return type type" { |
| 76 | var list: List(i32) = undefined; | 77 | var list: List(i32) = undefined; |
| 77 | var list2: List(i32) = undefined; | 78 | var list2: List(i32) = undefined; |
| 78 | list.length = 10; | 79 | list.length = 10; |
| ... | @@ -82,7 +83,7 @@ test "functionWithReturnTypeType" { | ... | @@ -82,7 +83,7 @@ test "functionWithReturnTypeType" { |
| 82 | } | 83 | } |
| 83 | 84 | ||
| 84 | 85 | ||
| 85 | test "genericStruct" { | 86 | test "generic struct" { |
| 86 | var a1 = GenNode(i32) {.value = 13, .next = null,}; | 87 | var a1 = GenNode(i32) {.value = 13, .next = null,}; |
| 87 | var b1 = GenNode(bool) {.value = true, .next = null,}; | 88 | var b1 = GenNode(bool) {.value = true, .next = null,}; |
| 88 | assert(a1.value == 13); | 89 | assert(a1.value == 13); |
| ... | @@ -97,7 +98,7 @@ fn GenNode(comptime T: type) -> type { | ... | @@ -97,7 +98,7 @@ fn GenNode(comptime T: type) -> type { |
| 97 | } | 98 | } |
| 98 | } | 99 | } |
| 99 | 100 | ||
| 100 | test "constDeclsInStruct" { | 101 | test "const decls in struct" { |
| 101 | assert(GenericDataThing(3).count_plus_one == 4); | 102 | assert(GenericDataThing(3).count_plus_one == 4); |
| 102 | } | 103 | } |
| 103 | fn GenericDataThing(comptime count: isize) -> type { | 104 | fn GenericDataThing(comptime count: isize) -> type { |
| ... | @@ -107,7 +108,7 @@ fn GenericDataThing(comptime count: isize) -> type { | ... | @@ -107,7 +108,7 @@ fn GenericDataThing(comptime count: isize) -> type { |
| 107 | } | 108 | } |
| 108 | 109 | ||
| 109 | 110 | ||
| 110 | test "useGenericParamInGenericParam" { | 111 | test "use generic param in generic param" { |
| 111 | assert(aGenericFn(i32, 3, 4) == 7); | 112 | assert(aGenericFn(i32, 3, 4) == 7); |
| 112 | } | 113 | } |
| 113 | fn aGenericFn(comptime T: type, comptime a: T, b: T) -> T { | 114 | fn aGenericFn(comptime T: type, comptime a: T, b: T) -> T { |
| ... | @@ -115,7 +116,7 @@ fn aGenericFn(comptime T: type, comptime a: T, b: T) -> T { | ... | @@ -115,7 +116,7 @@ fn aGenericFn(comptime T: type, comptime a: T, b: T) -> T { |
| 115 | } | 116 | } |
| 116 | 117 | ||
| 117 | 118 | ||
| 118 | test "genericFnWithImplicitCast" { | 119 | test "generic fn with implicit cast" { |
| 119 | assert(getFirstByte(u8, []u8 {13}) == 13); | 120 | assert(getFirstByte(u8, []u8 {13}) == 13); |
| 120 | assert(getFirstByte(u16, []u16 {0, 13}) == 0); | 121 | assert(getFirstByte(u16, []u16 {0, 13}) == 0); |
| 121 | } | 122 | } |
| ... | @@ -123,3 +124,14 @@ fn getByte(ptr: ?&const u8) -> u8 {*??ptr} | ... | @@ -123,3 +124,14 @@ fn getByte(ptr: ?&const u8) -> u8 {*??ptr} |
| 123 | fn getFirstByte(comptime T: type, mem: []const T) -> u8 { | 124 | fn getFirstByte(comptime T: type, mem: []const T) -> u8 { |
| 124 | getByte(@ptrCast(&const u8, &mem[0])) | 125 | getByte(@ptrCast(&const u8, &mem[0])) |
| 125 | } | 126 | } |
| 127 | |||
| 128 | |||
| 129 | const foos = []fn(var) -> bool { foo1, foo2 }; | ||
| 130 | |||
| 131 | fn foo1(arg: var) -> bool { arg } | ||
| 132 | fn foo2(arg: var) -> bool { !arg } | ||
| 133 | |||
| 134 | test "array of generic fns" { | ||
| 135 | assert(foos[0](true)); | ||
| 136 | assert(!foos[1](true)); | ||
| 137 | } |
test/cases/var_args.zig+11| ... | @@ -54,3 +54,14 @@ fn extraFn(extra: u32, args: ...) -> usize { | ... | @@ -54,3 +54,14 @@ fn extraFn(extra: u32, args: ...) -> usize { |
| 54 | } | 54 | } |
| 55 | return args.len; | 55 | return args.len; |
| 56 | } | 56 | } |
| 57 | |||
| 58 | |||
| 59 | const foos = []fn(...) -> bool { foo1, foo2 }; | ||
| 60 | |||
| 61 | fn foo1(args: ...) -> bool { true } | ||
| 62 | fn foo2(args: ...) -> bool { false } | ||
| 63 | |||
| 64 | test "array of var args functions" { | ||
| 65 | assert(foos[0]()); | ||
| 66 | assert(!foos[1]()); | ||
| 67 | } |
test/compile_errors.zig+12| ... | @@ -1904,4 +1904,16 @@ pub fn addCases(cases: &tests.CompileErrorContext) { | ... | @@ -1904,4 +1904,16 @@ pub fn addCases(cases: &tests.CompileErrorContext) { |
| 1904 | \\} | 1904 | \\} |
| 1905 | , | 1905 | , |
| 1906 | ".tmp_source.zig:7:9: error: calling a generic function requires compile-time known function value"); | 1906 | ".tmp_source.zig:7:9: error: calling a generic function requires compile-time known function value"); |
| 1907 | |||
| 1908 | cases.add("calling a generic function only known at runtime", | ||
| 1909 | \\var foos = []fn(var) { foo1, foo2 }; | ||
| 1910 | \\ | ||
| 1911 | \\fn foo1(arg: var) {} | ||
| 1912 | \\fn foo2(arg: var) {} | ||
| 1913 | \\ | ||
| 1914 | \\pub fn main() -> %void { | ||
| 1915 | \\ foos[0](true); | ||
| 1916 | \\} | ||
| 1917 | , | ||
| 1918 | ".tmp_source.zig:7:9: error: calling a generic function requires compile-time known function value"); | ||
| 1907 | } | 1919 | } |