From ede76f4fe31e6fc935ae10f030eb2c97ed36aaaa Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 6 Aug 2021 16:24:39 -0700 Subject: stage2: fix generics with non-comptime anytype parameters The `comptime_args` field of Fn has a clarified purpose: For generic function instantiations, there is a `TypedValue` here for each parameter of the function: * Non-comptime parameters are marked with a `generic_poison` for the value. * Non-anytype parameters are marked with a `generic_poison` for the type. Sema now has a `fn_ret_ty` field. Doc comments reproduced here: > When semantic analysis needs to know the return type of the function whose body > is being analyzed, this `Type` should be used instead of going through `func`. > This will correctly handle the case of a comptime/inline function call of a > generic function which uses a type expression for the return type. > The type will be `void` in the case that `func` is `null`. Various places in Sema are modified in accordance with this guidance. Fixed `resolveMaybeUndefVal` not returning `error.GenericPoison` when Value Tag of `generic_poison` is encountered. Fixed generic function memoization incorrect equality checking. The logic now clearly deals properly with any combination of anytype and comptime parameters. Fixed not removing generic function instantiation from the table in case a compile errors in the rest of `call` semantic analysis. This required introduction of yet another adapter which I have called `GenericRemoveAdapter`. This one is nice and simple - it's the same hash function (the same precomputed hash is passed in) but the equality function checks pointers rather than doing any logic. Inline/comptime function calls coerce each argument in accordance with the function parameter type expressions. Likewise the return type expression is evaluated and provided (see `fn_ret_ty` above). There's a new compile error "unable to monomorphize function". It's pretty unhelpful and will need to get improved in the future. It happens when a type expression in a generic function did not end up getting resolved at a callsite. This can happen, for example, if a runtime parameter is attempted to be used where it needed to be comptime known: ```zig fn foo(x: anytype) [x]u8 { _ = x; } ``` In this example, even if we pass a number such as `10` for `x`, it is not marked `comptime`, so `x` will have a runtime known value, making the return type unable to resolve. In the LLVM backend I implement cmp instructions for float types to pass some behavior tests that used floats. --- src/Module.zig | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src/Module.zig') diff --git a/src/Module.zig b/src/Module.zig index a694f775da..3652a27927 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -801,8 +801,9 @@ pub const Fn = struct { /// The Decl that corresponds to the function itself. owner_decl: *Decl, /// If this is not null, this function is a generic function instantiation, and - /// there is a `Value` here for each parameter of the function. Non-comptime - /// parameters are marked with an `unreachable_value`. + /// there is a `TypedValue` here for each parameter of the function. + /// Non-comptime parameters are marked with a `generic_poison` for the value. + /// Non-anytype parameters are marked with a `generic_poison` for the type. comptime_args: ?[*]TypedValue = null, /// The ZIR instruction that is a function instruction. Use this to find /// the body. We store this rather than the body directly so that when ZIR @@ -2975,6 +2976,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void { .owner_decl = new_decl, .namespace = &struct_obj.namespace, .func = null, + .fn_ret_ty = Type.initTag(.void), .owner_func = null, }; defer sema.deinit(); @@ -3029,6 +3031,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { .owner_decl = decl, .namespace = decl.namespace, .func = null, + .fn_ret_ty = Type.initTag(.void), .owner_func = null, }; defer sema.deinit(); @@ -3712,6 +3715,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air { .owner_decl = decl, .namespace = decl.namespace, .func = func, + .fn_ret_ty = func.owner_decl.ty.fnReturnType(), .owner_func = func, }; defer sema.deinit(); @@ -3764,7 +3768,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air { }; if (func.comptime_args) |comptime_args| { const arg_tv = comptime_args[total_param_index]; - if (arg_tv.val.tag() != .unreachable_value) { + if (arg_tv.val.tag() != .generic_poison) { // We have a comptime value for this parameter. const arg = try sema.addConstant(arg_tv.ty, arg_tv.val); sema.inst_map.putAssumeCapacityNoClobber(inst, arg); @@ -4447,6 +4451,7 @@ pub fn analyzeStructFields(mod: *Module, struct_obj: *Struct) CompileError!void .namespace = &struct_obj.namespace, .owner_func = null, .func = null, + .fn_ret_ty = Type.initTag(.void), }; defer sema.deinit(); @@ -4600,6 +4605,7 @@ pub fn analyzeUnionFields(mod: *Module, union_obj: *Union) CompileError!void { .namespace = &union_obj.namespace, .owner_func = null, .func = null, + .fn_ret_ty = Type.initTag(.void), }; defer sema.deinit(); -- cgit v1.2.3