Sema: rewrite semantic analysis of function calls
This rewrite improves some error messages, hugely simplifies the logic, and fixes several bugs. One of these bugs is technically a new rule which Andrew and I agreed on: if a parameter has a comptime-only type but is not declared `comptime`, then the corresponding call argument should not be *evaluated* at comptime; only resolved. Implementing this required changing how function types work a little, which in turn required allowing a new kind of function coercion for some generic use cases: function coercions are now allowed to implicitly *remove* `comptime` annotations from parameters with comptime-only types. This is okay because removing the annotation affects only the call site. Resolves: #22262
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
//! Whew, that filename is a bit of a mouthful!
|
||||
//! To maximise consistency with other parts of the language, function arguments expressions are
|
||||
//! only *evaluated* at comptime if the parameter is declared `comptime`. If the parameter type is
|
||||
//! comptime-only, but the parameter is not declared `comptime`, the evaluation happens at runtime,
|
||||
//! and the value is just comptime-resolved.
|
||||
|
||||
export fn foo() void {
|
||||
// This function is itself generic, with the comptime-only parameter being generic.
|
||||
simpleGeneric(type, if (cond()) u8 else u16);
|
||||
}
|
||||
|
||||
export fn bar() void {
|
||||
// This function is not generic; once `Wrapper` is called, its parameter type is immediately known.
|
||||
Wrapper(type).inner(if (cond()) u8 else u16);
|
||||
}
|
||||
|
||||
fn simpleGeneric(comptime T: type, _: T) void {}
|
||||
|
||||
fn Wrapper(comptime T: type) type {
|
||||
return struct {
|
||||
fn inner(_: T) void {}
|
||||
};
|
||||
}
|
||||
|
||||
fn cond() bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
// error
|
||||
//
|
||||
// :9:25: error: value with comptime-only type 'type' depends on runtime control flow
|
||||
// :9:33: note: runtime control flow here
|
||||
// :9:25: note: types are not available at runtime
|
||||
// :14:25: error: value with comptime-only type 'type' depends on runtime control flow
|
||||
// :14:33: note: runtime control flow here
|
||||
// :14:25: note: types are not available at runtime
|
||||
@@ -42,8 +42,8 @@ noinline fn dummy2() void {}
|
||||
// :2:23: error: expected a tuple, found 'void'
|
||||
// :5:21: error: unable to perform 'never_inline' call at compile-time
|
||||
// :8:21: error: unable to perform 'never_tail' call at compile-time
|
||||
// :11:5: error: 'never_inline' call of inline function
|
||||
// :11:5: error: cannot perform inline call with 'never_inline' modifier
|
||||
// :15:26: error: modifier 'compile_time' requires a comptime-known function
|
||||
// :18:9: error: 'always_inline' call of noinline function
|
||||
// :21:9: error: 'always_inline' call of noinline function
|
||||
// :18:9: error: inline call of noinline function
|
||||
// :21:9: error: inline call of noinline function
|
||||
// :26:27: error: modifier 'always_inline' requires a comptime-known function
|
||||
|
||||
@@ -4,7 +4,6 @@ export fn entry() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :3:20: error: comptime call of function pointer
|
||||
// :3:14: error: unable to resolve comptime value
|
||||
// :3:14: note: function being called at comptime must be comptime-known
|
||||
|
||||
@@ -36,11 +36,13 @@ pub export fn entry2() void {
|
||||
//
|
||||
// :8:9: error: unable to resolve comptime value
|
||||
// :19:15: note: called at comptime from here
|
||||
// :7:13: note: function with comptime-only return type 'tmp.S' is evaluated at comptime
|
||||
// :19:15: note: call to function with comptime-only return type 'tmp.S' is evaluated at comptime
|
||||
// :7:13: note: return type declared here
|
||||
// :2:12: note: struct requires comptime because of this field
|
||||
// :2:12: note: use '*const fn () void' for a function pointer type
|
||||
// :22:13: error: unable to resolve comptime value
|
||||
// :32:19: note: called at comptime from here
|
||||
// :21:17: note: function with comptime-only return type 'tmp.S' is evaluated at comptime
|
||||
// :32:19: note: call to function with comptime-only return type 'tmp.S' is evaluated at comptime
|
||||
// :21:17: note: return type declared here
|
||||
// :2:12: note: struct requires comptime because of this field
|
||||
// :2:12: note: use '*const fn () void' for a function pointer type
|
||||
|
||||
@@ -1,54 +1,7 @@
|
||||
const std = @import("std");
|
||||
|
||||
const Error = error{Something};
|
||||
|
||||
fn next() Error!void {
|
||||
return;
|
||||
}
|
||||
|
||||
fn parse(comptime T: type, allocator: std.mem.Allocator) !void {
|
||||
parseFree(T, undefined, allocator);
|
||||
_ = (try next()) != null;
|
||||
}
|
||||
|
||||
fn parseFree(comptime T: type, value: T, allocator: std.mem.Allocator) void {
|
||||
switch (@typeInfo(T)) {
|
||||
.@"struct" => |structInfo| {
|
||||
inline for (structInfo.fields) |field| {
|
||||
if (!field.is_comptime)
|
||||
parseFree(field.type, undefined, allocator);
|
||||
}
|
||||
},
|
||||
.pointer => |ptrInfo| {
|
||||
switch (ptrInfo.size) {
|
||||
.One => {
|
||||
parseFree(ptrInfo.child, value.*, allocator);
|
||||
},
|
||||
.Slice => {
|
||||
for (value) |v|
|
||||
parseFree(ptrInfo.child, v, allocator);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub export fn entry() void {
|
||||
const allocator = std.testing.failing_allocator;
|
||||
_ = parse(std.StringArrayHashMap(bool), allocator) catch return;
|
||||
export fn foo(ptr: *anyopaque) void {
|
||||
_ = ptr.*;
|
||||
}
|
||||
|
||||
// error
|
||||
// target=native
|
||||
// backend=llvm
|
||||
//
|
||||
// :11:22: error: comparison of 'void' with null
|
||||
// :25:51: error: cannot load opaque type 'anyopaque'
|
||||
// :25:51: error: values of type 'fn (*anyopaque, usize, u8, usize) ?[*]u8' must be comptime-known, but operand value is runtime-known
|
||||
// :25:51: note: use '*const fn (*anyopaque, usize, u8, usize) ?[*]u8' for a function pointer type
|
||||
// :25:51: error: values of type 'fn (*anyopaque, []u8, u8, usize, usize) bool' must be comptime-known, but operand value is runtime-known
|
||||
// :25:51: note: use '*const fn (*anyopaque, []u8, u8, usize, usize) bool' for a function pointer type
|
||||
// :25:51: error: values of type 'fn (*anyopaque, []u8, u8, usize) void' must be comptime-known, but operand value is runtime-known
|
||||
// :25:51: note: use '*const fn (*anyopaque, []u8, u8, usize) void' for a function pointer type
|
||||
// :2:12: error: cannot load opaque type 'anyopaque'
|
||||
|
||||
@@ -15,6 +15,7 @@ pub export fn entry() void {
|
||||
// error
|
||||
//
|
||||
// :12:13: error: unable to resolve comptime value
|
||||
// :7:16: note: function with comptime-only return type 'tmp.S' is evaluated at comptime
|
||||
// :12:12: note: call to function with comptime-only return type 'tmp.S' is evaluated at comptime
|
||||
// :7:16: note: return type declared here
|
||||
// :2:12: note: struct requires comptime because of this field
|
||||
// :2:12: note: use '*const fn () void' for a function pointer type
|
||||
|
||||
@@ -17,6 +17,7 @@ pub export fn entry() void {
|
||||
// error
|
||||
//
|
||||
// :15:13: error: unable to resolve comptime value
|
||||
// :9:38: note: generic function instantiated with comptime-only return type 'tmp.S(fn () void)' is evaluated at comptime
|
||||
// :15:12: note: call to generic function instantiated with comptime-only return type 'tmp.S(fn () void)' is evaluated at comptime
|
||||
// :9:38: note: return type declared here
|
||||
// :3:16: note: struct requires comptime because of this field
|
||||
// :3:16: note: use '*const fn () void' for a function pointer type
|
||||
|
||||
@@ -10,8 +10,7 @@ export fn entry() usize {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :5:16: error: runtime-known argument passed to comptime parameter
|
||||
// :1:17: note: declared comptime here
|
||||
// :5:16: error: unable to resolve comptime value
|
||||
// :5:16: note: argument to comptime parameter must be comptime-known
|
||||
// :1:8: note: parameter declared comptime here
|
||||
|
||||
@@ -22,9 +22,8 @@ fn Type(comptime n: usize) type {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :21:16: error: evaluation exceeded 1001 backwards branches
|
||||
// :21:16: note: use @setEvalBranchQuota() to raise the branch limit from 1001
|
||||
// :16:34: note: called from here
|
||||
// :8:15: note: called from here
|
||||
|
||||
@@ -40,3 +40,4 @@ pub fn is(comptime id: std.builtin.TypeId) TraitFn {
|
||||
// target=native
|
||||
//
|
||||
// :8:48: error: expected type 'type', found 'bool'
|
||||
// :5:21: note: called from here
|
||||
|
||||
@@ -22,12 +22,11 @@ const S = struct {
|
||||
};
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :3:18: error: expected type 'bool', found 'void'
|
||||
// :19:43: note: parameter type declared here
|
||||
// :8:18: error: expected type 'void', found 'bool'
|
||||
// :20:43: note: parameter type declared here
|
||||
// :15:26: error: runtime-known argument passed to comptime parameter
|
||||
// :21:57: note: declared comptime here
|
||||
// :15:26: error: unable to resolve comptime value
|
||||
// :15:26: note: argument to comptime parameter must be comptime-known
|
||||
// :21:48: note: parameter declared comptime here
|
||||
|
||||
@@ -5,7 +5,5 @@ export fn entry() i32 {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :2:14: error: comptime call of extern function
|
||||
|
||||
@@ -10,8 +10,7 @@ pub export fn entry() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :5:18: error: unable to resolve comptime value
|
||||
// :5:18: note: argument to comptime parameter must be comptime-known
|
||||
// :1:24: note: parameter declared comptime here
|
||||
|
||||
@@ -9,8 +9,6 @@ export fn entry1() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :4:15: error: comptime call of extern function pointer
|
||||
// :8:5: error: inline call of extern function pointer
|
||||
// :4:15: error: comptime call of extern function
|
||||
// :8:5: error: inline call of extern function
|
||||
|
||||
@@ -7,7 +7,5 @@ export fn f() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :2:16: error: comptime call of extern function
|
||||
|
||||
@@ -19,6 +19,6 @@ pub export fn entry() void {
|
||||
// backend=llvm
|
||||
// target=native
|
||||
//
|
||||
// :15:28: error: expected type '*const fn (comptime type, u8, u8) u32', found '*const fn (void, u8, u8) u32'
|
||||
// :15:28: note: pointer type child 'fn (void, u8, u8) u32' cannot cast into pointer type child 'fn (comptime type, u8, u8) u32'
|
||||
// :15:28: error: expected type '*const fn (type, u8, u8) u32', found '*const fn (void, u8, u8) u32'
|
||||
// :15:28: note: pointer type child 'fn (void, u8, u8) u32' cannot cast into pointer type child 'fn (type, u8, u8) u32'
|
||||
// :15:28: note: non-generic function cannot cast into a generic function
|
||||
|
||||
@@ -19,5 +19,5 @@ export fn entry2() void {
|
||||
|
||||
// error
|
||||
//
|
||||
// :14:5: error: 'never_inline' call of inline function
|
||||
// :17:5: error: 'never_inline' call of inline function
|
||||
// :14:5: error: cannot perform inline call with 'never_inline' modifier
|
||||
// :17:5: error: cannot perform inline call with 'never_inline' modifier
|
||||
|
||||
@@ -9,7 +9,5 @@ export fn entry() usize {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :4:27: error: comptime call of extern function
|
||||
|
||||
@@ -11,5 +11,6 @@ export fn entry() void {
|
||||
// error
|
||||
//
|
||||
// :8:11: error: unable to resolve comptime value
|
||||
// :1:20: note: function with comptime-only return type 'type' is evaluated at comptime
|
||||
// :1:20: note: types are not available at runtime
|
||||
// :8:10: note: call to function with comptime-only return type 'type' is evaluated at comptime
|
||||
// :1:20: note: return type declared here
|
||||
// :8:10: note: types are not available at runtime
|
||||
|
||||
@@ -29,8 +29,10 @@ pub export fn entry2() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :5:27: error: inline call is recursive
|
||||
// :12:12: note: called from here
|
||||
// :24:10: error: inline call is recursive
|
||||
// :20:10: note: called from here
|
||||
// :16:11: note: called from here
|
||||
// :28:10: note: called from here
|
||||
|
||||
@@ -27,8 +27,9 @@ var rt: u32 = undefined;
|
||||
// :19:5: note: operation is runtime due to this operand
|
||||
// :14:8: note: called at comptime from here
|
||||
// :10:12: note: called at comptime from here
|
||||
// :13:10: note: function with comptime-only return type 'type' is evaluated at comptime
|
||||
// :13:10: note: types are not available at runtime
|
||||
// :10:12: note: call to function with comptime-only return type 'type' is evaluated at comptime
|
||||
// :13:10: note: return type declared here
|
||||
// :10:12: note: types are not available at runtime
|
||||
// :2:8: note: called from here
|
||||
// :19:8: error: unable to evaluate comptime expression
|
||||
// :19:5: note: operation is runtime due to this operand
|
||||
|
||||
Reference in New Issue
Block a user