Commit b55a5007fa

Veikka Tuominen <git@vexu.eu>
2022-08-21 17:04:46
Sema: fix parameter of type 'T' must be comptime error
Closes #12519 Closes #12505
1 parent b2f02a8
doc/langref.html.in
@@ -5023,8 +5023,8 @@ fn shiftLeftOne(a: u32) callconv(.Inline) u32 {
 // Another file can use @import and call sub2
 pub fn sub2(a: i8, b: i8) i8 { return a - b; }
 
-// Functions can be used as values and are equivalent to pointers.
-const call2_op = fn (a: i8, b: i8) i8;
+// Function pointers are prefixed with `*const `.
+const call2_op = *const fn (a: i8, b: i8) i8;
 fn do_op(fn_call: call2_op, op1: i8, op2: i8) i8 {
     return fn_call(op1, op2);
 }
lib/std/fs/path.zig
@@ -42,7 +42,7 @@ pub fn isSep(byte: u8) bool {
 
 /// This is different from mem.join in that the separator will not be repeated if
 /// it is found at the end or beginning of a pair of consecutive paths.
-fn joinSepMaybeZ(allocator: Allocator, separator: u8, sepPredicate: fn (u8) bool, paths: []const []const u8, zero: bool) ![]u8 {
+fn joinSepMaybeZ(allocator: Allocator, separator: u8, comptime sepPredicate: fn (u8) bool, paths: []const []const u8, zero: bool) ![]u8 {
     if (paths.len == 0) return if (zero) try allocator.dupe(u8, &[1]u8{0}) else &[0]u8{};
 
     // Find first non-empty path index.
lib/std/math/float.zig
@@ -8,7 +8,7 @@ inline fn mantissaOne(comptime T: type) comptime_int {
 }
 
 /// Creates floating point type T from an unbiased exponent and raw mantissa.
-inline fn reconstructFloat(comptime T: type, exponent: comptime_int, mantissa: comptime_int) T {
+inline fn reconstructFloat(comptime T: type, comptime exponent: comptime_int, comptime mantissa: comptime_int) T {
     const TBits = @Type(.{ .Int = .{ .signedness = .unsigned, .bits = @bitSizeOf(T) } });
     const biased_exponent = @as(TBits, exponent + floatExponentMax(T));
     return @bitCast(T, (biased_exponent << floatMantissaBits(T)) | @as(TBits, mantissa));
lib/std/zig/c_translation.zig
@@ -349,7 +349,7 @@ test "shuffleVectorIndex" {
 
 /// Constructs a [*c] pointer with the const and volatile annotations
 /// from SelfType for pointing to a C flexible array of ElementType.
-pub fn FlexibleArrayType(comptime SelfType: type, ElementType: type) type {
+pub fn FlexibleArrayType(comptime SelfType: type, comptime ElementType: type) type {
     switch (@typeInfo(SelfType)) {
         .Pointer => |ptr| {
             return @Type(.{ .Pointer = .{
lib/std/zig/parse.zig
@@ -3670,7 +3670,7 @@ const Parser = struct {
     }
 
     /// KEYWORD_if LPAREN Expr RPAREN PtrPayload? Body (KEYWORD_else Payload? Body)?
-    fn parseIf(p: *Parser, bodyParseFn: fn (p: *Parser) Error!Node.Index) !Node.Index {
+    fn parseIf(p: *Parser, comptime bodyParseFn: fn (p: *Parser) Error!Node.Index) !Node.Index {
         const if_token = p.eatToken(.keyword_if) orelse return null_node;
         _ = try p.expectToken(.l_paren);
         const condition = try p.expectExpr();
lib/std/math.zig
@@ -1548,7 +1548,7 @@ test "boolMask" {
 }
 
 /// Return the mod of `num` with the smallest integer type
-pub fn comptimeMod(num: anytype, denom: comptime_int) IntFittingRange(0, denom - 1) {
+pub fn comptimeMod(num: anytype, comptime denom: comptime_int) IntFittingRange(0, denom - 1) {
     return @intCast(IntFittingRange(0, denom - 1), @mod(num, denom));
 }
 
src/Module.zig
@@ -6072,17 +6072,17 @@ pub fn paramSrc(
         else => unreachable,
     };
     var it = full.iterate(tree);
-    while (true) {
-        if (it.param_i == param_i) {
-            const param = it.next().?;
+    var i: usize = 0;
+    while (it.next()) |param| : (i += 1) {
+        if (i == param_i) {
             if (param.anytype_ellipsis3) |some| {
                 const main_token = tree.nodes.items(.main_token)[decl.src_node];
                 return .{ .token_offset_param = @bitCast(i32, some) - @bitCast(i32, main_token) };
             }
             return .{ .node_offset_param = decl.nodeIndexToRelative(param.type_expr) };
         }
-        _ = it.next();
     }
+    unreachable;
 }
 
 pub fn argSrc(
src/Sema.zig
@@ -76,6 +76,8 @@ types_to_resolve: std.ArrayListUnmanaged(Air.Inst.Ref) = .{},
 post_hoc_blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, *LabeledBlock) = .{},
 /// Populated with the last compile error created.
 err: ?*Module.ErrorMsg = null,
+/// True when analyzing a generic instantiation. Used to suppress some errors.
+is_generic_instantiation: bool = false,
 
 const std = @import("std");
 const math = std.math;
@@ -6495,6 +6497,7 @@ fn instantiateGenericCall(
             .comptime_args = try new_decl_arena_allocator.alloc(TypedValue, uncasted_args.len),
             .comptime_args_fn_inst = module_fn.zir_body_inst,
             .preallocated_new_func = new_module_func,
+            .is_generic_instantiation = true,
         };
         defer child_sema.deinit();
 
@@ -7789,6 +7792,7 @@ fn funcCommon(
                 &is_generic,
                 is_extern,
                 cc_workaround,
+                has_body,
             ) catch |err| switch (err) {
                 error.NeededSourceLocation => {
                     const decl = sema.mod.declPtr(block.src_decl);
@@ -7802,6 +7806,7 @@ fn funcCommon(
                         &is_generic,
                         is_extern,
                         cc_workaround,
+                        has_body,
                     );
                     return error.AnalysisFail;
                 },
@@ -8005,6 +8010,7 @@ fn analyzeParameter(
     is_generic: *bool,
     is_extern: bool,
     cc: std.builtin.CallingConvention,
+    has_body: bool,
 ) !void {
     const requires_comptime = try sema.typeRequiresComptime(block, param_src, param.ty);
     comptime_params[i] = param.is_comptime or requires_comptime;
@@ -8053,9 +8059,9 @@ fn analyzeParameter(
         };
         return sema.failWithOwnedErrorMsg(msg);
     }
-    if (requires_comptime and !param.is_comptime) {
+    if (!sema.is_generic_instantiation and requires_comptime and !param.is_comptime and has_body) {
         const msg = msg: {
-            const msg = try sema.errMsg(block, param_src, "parametter of type '{}' must be declared comptime", .{
+            const msg = try sema.errMsg(block, param_src, "parameter of type '{}' must be declared comptime", .{
                 param.ty.fmt(sema.mod),
             });
             errdefer msg.destroy(sema.gpa);
@@ -8153,7 +8159,7 @@ fn zirParam(
 
     try block.params.append(sema.gpa, .{
         .ty = param_ty,
-        .is_comptime = is_comptime,
+        .is_comptime = comptime_syntax,
         .name = param_name,
     });
     const result = try sema.addConstant(param_ty, Value.initTag(.generic_poison));
@@ -16318,7 +16324,7 @@ fn zirUnaryMath(
     block: *Block,
     inst: Zir.Inst.Index,
     air_tag: Air.Inst.Tag,
-    eval: fn (Value, Type, Allocator, std.Target) Allocator.Error!Value,
+    comptime eval: fn (Value, Type, Allocator, std.Target) Allocator.Error!Value,
 ) CompileError!Air.Inst.Ref {
     const tracy = trace(@src());
     defer tracy.end();
@@ -17777,7 +17783,7 @@ fn zirBitCount(
     block: *Block,
     inst: Zir.Inst.Index,
     air_tag: Air.Inst.Tag,
-    comptimeOp: fn (val: Value, ty: Type, target: std.Target) u64,
+    comptime comptimeOp: fn (val: Value, ty: Type, target: std.Target) u64,
 ) CompileError!Air.Inst.Ref {
     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
     const src = inst_data.src();
@@ -29491,7 +29497,7 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ
         => {
             const child_ty = ty.childType();
             if (child_ty.zigTypeTag() == .Fn) {
-                return false;
+                return child_ty.fnInfo().is_generic;
             } else {
                 return sema.typeRequiresComptime(block, src, child_ty);
             }
src/type.zig
@@ -2394,7 +2394,7 @@ pub const Type = extern union {
                 if (ignore_comptime_only) {
                     return true;
                 } else if (ty.childType().zigTypeTag() == .Fn) {
-                    return true;
+                    return !ty.childType().fnInfo().is_generic;
                 } else if (sema_kit) |sk| {
                     return !(try sk.sema.typeRequiresComptime(sk.block, sk.src, ty));
                 } else {
test/behavior/cast.zig
@@ -1281,7 +1281,7 @@ test "*const [N]null u8 to ?[]const u8" {
 test "cast between [*c]T and ?[*:0]T on fn parameter" {
     const S = struct {
         const Handler = ?fn ([*c]const u8) callconv(.C) void;
-        fn addCallback(handler: Handler) void {
+        fn addCallback(comptime handler: Handler) void {
             _ = handler;
         }
 
test/behavior/error.zig
@@ -168,7 +168,7 @@ fn entryPtr() void {
     fooPtr(ptr);
 }
 
-fn foo2(f: fn () anyerror!void) void {
+fn foo2(comptime f: fn () anyerror!void) void {
     const x = f();
     x catch {
         @panic("fail");
test/behavior/fn.zig
@@ -137,7 +137,7 @@ test "implicit cast function unreachable return" {
     wantsFnWithVoid(fnWithUnreachable);
 }
 
-fn wantsFnWithVoid(f: fn () void) void {
+fn wantsFnWithVoid(comptime f: fn () void) void {
     _ = f;
 }
 
test/behavior/struct.zig
@@ -147,7 +147,7 @@ test "fn call of struct field" {
             return 13;
         }
 
-        fn callStructField(foo: Foo) i32 {
+        fn callStructField(comptime foo: Foo) i32 {
             return foo.ptr();
         }
     };
test/cases/compile_errors/comptime_parameter_not_declared_as_such.zig
@@ -0,0 +1,23 @@
+fn f(_: anytype) void {}
+fn g(h: *const fn (anytype) void) void {
+    h({});
+}
+pub export fn entry() void {
+    g(f);
+}
+
+pub fn comptimeMod(num: anytype, denom: comptime_int) void {
+    _ = num;
+    _ = denom;
+}
+
+pub export fn entry1() void {
+    _ = comptimeMod(1, 2);
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :2:6: error: parameter of type '*const fn(anytype) void' must be declared comptime
+// :9:34: error: parameter of type 'comptime_int' must be declared comptime