Sema: add error for non-comptime param in comptime func

Adds error for taking a non comptime parameter in a function returning a
comptime-only type but not when that type is dependent on a parameter.

Co-authored-by: Veikka Tuominen <git@vexu.eu>
This commit is contained in:
antlilja 2022-08-06 22:32:00 +02:00 committed by Veikka Tuominen
parent da95da438e
commit ae8d26a6a0
10 changed files with 95 additions and 26 deletions

View File

@ -20,7 +20,7 @@ pub const Tokenizer = tokenizer.Tokenizer;
/// If linking gnu libc (glibc), the `ok` value will be true if the target
/// version is greater than or equal to `glibc_version`.
/// If linking a libc other than these, returns `false`.
pub fn versionCheck(glibc_version: std.builtin.Version) type {
pub fn versionCheck(comptime glibc_version: std.builtin.Version) type {
return struct {
pub const ok = blk: {
if (!builtin.link_libc) break :blk false;

View File

@ -406,7 +406,7 @@ pub const Header = struct {
}
};
pub fn ProgramHeaderIterator(ParseSource: anytype) type {
pub fn ProgramHeaderIterator(comptime ParseSource: anytype) type {
return struct {
elf_header: Header,
parse_source: ParseSource,
@ -456,7 +456,7 @@ pub fn ProgramHeaderIterator(ParseSource: anytype) type {
};
}
pub fn SectionHeaderIterator(ParseSource: anytype) type {
pub fn SectionHeaderIterator(comptime ParseSource: anytype) type {
return struct {
elf_header: Header,
parse_source: ParseSource,

View File

@ -7,7 +7,7 @@ const meta = std.meta;
const math = std.math;
/// Creates a stream which allows for reading bit fields from another stream
pub fn BitReader(endian: std.builtin.Endian, comptime ReaderType: type) type {
pub fn BitReader(comptime endian: std.builtin.Endian, comptime ReaderType: type) type {
return struct {
forward_reader: ReaderType,
bit_buffer: u7,

View File

@ -7,7 +7,7 @@ const meta = std.meta;
const math = std.math;
/// Creates a stream which allows for writing bit fields to another stream
pub fn BitWriter(endian: std.builtin.Endian, comptime WriterType: type) type {
pub fn BitWriter(comptime endian: std.builtin.Endian, comptime WriterType: type) type {
return struct {
forward_writer: WriterType,
bit_buffer: u8,

View File

@ -764,7 +764,7 @@ const TagPayloadType = TagPayload;
///Given a tagged union type, and an enum, return the type of the union
/// field corresponding to the enum tag.
pub fn TagPayload(comptime U: type, tag: Tag(U)) type {
pub fn TagPayload(comptime U: type, comptime tag: Tag(U)) type {
comptime debug.assert(trait.is(.Union)(U));
const info = @typeInfo(U).Union;

View File

@ -459,7 +459,7 @@ pub fn MultiArrayList(comptime S: type) type {
return self.bytes[0..capacityInBytes(self.capacity)];
}
fn FieldType(field: Field) type {
fn FieldType(comptime field: Field) type {
return meta.fieldInfo(S, field).field_type;
}

View File

@ -7816,19 +7816,17 @@ fn funcCommon(
};
}
var is_comptime_ret = false;
const ret_poison = if (!is_generic) rp: {
if (sema.typeRequiresComptime(block, ret_ty_src, bare_return_type)) |ret_comptime| {
is_comptime_ret = ret_comptime;
break :rp bare_return_type.tag() == .generic_poison;
} else |err| switch (err) {
error.GenericPoison => {
is_generic = true;
break :rp true;
},
else => |e| return e,
}
} else bare_return_type.tag() == .generic_poison;
var ret_ty_requires_comptime = false;
const ret_poison = if (sema.typeRequiresComptime(block, ret_ty_src, bare_return_type)) |ret_comptime| rp: {
ret_ty_requires_comptime = ret_comptime;
break :rp bare_return_type.tag() == .generic_poison;
} else |err| switch (err) {
error.GenericPoison => rp: {
is_generic = true;
break :rp true;
},
else => |e| return e,
};
const return_type = if (!inferred_error_set or ret_poison)
bare_return_type
@ -7873,6 +7871,41 @@ fn funcCommon(
return sema.failWithOwnedErrorMsg(msg);
}
// If the return type is comptime only but not dependent on parameters then all parameter types also need to be comptime
if (!sema.is_generic_instantiation and has_body and ret_ty_requires_comptime) comptime_check: {
for (block.params.items) |param| {
if (!param.is_comptime) break;
} else break :comptime_check;
const msg = try sema.errMsg(
block,
ret_ty_src,
"function with comptime only return type '{}' requires all parameters to be comptime",
.{return_type.fmt(sema.mod)},
);
try sema.explainWhyTypeIsComptime(block, ret_ty_src, msg, ret_ty_src.toSrcLoc(sema.owner_decl), return_type);
const tags = sema.code.instructions.items(.tag);
const data = sema.code.instructions.items(.data);
const param_body = sema.code.getParamBody(func_inst);
for (block.params.items) |param, i| {
if (!param.is_comptime) {
const param_index = param_body[i];
const param_src = switch (tags[param_index]) {
.param => data[param_index].pl_tok.src(),
.param_anytype => data[param_index].str_tok.src(),
else => unreachable,
};
if (param.name.len != 0) {
try sema.errNote(block, param_src, msg, "param '{s}' is required to be comptime", .{param.name});
} else {
try sema.errNote(block, param_src, msg, "param is required to be comptime", .{});
}
}
}
return sema.failWithOwnedErrorMsg(msg);
}
const arch = sema.mod.getTarget().cpu.arch;
if (switch (cc_workaround) {
.Unspecified, .C, .Naked, .Async, .Inline => null,
@ -7917,7 +7950,7 @@ fn funcCommon(
}
if (is_generic and sema.no_partial_func_ty) return error.GenericPoison;
for (comptime_params) |ct| is_generic = is_generic or ct;
is_generic = is_generic or is_comptime_ret;
is_generic = is_generic or ret_ty_requires_comptime;
break :fn_ty try Type.Tag.function.create(sema.arena, .{
.param_types = param_types,

View File

@ -745,7 +745,7 @@ fn setAttribute(attr: Attribute) void {
_ = attr;
}
fn Setter(attr: Attribute) type {
fn Setter(comptime attr: Attribute) type {
return struct {
fn set() void {
setAttribute(attr);

View File

@ -4,12 +4,12 @@ const S = struct {
};
fn bar() void {}
fn foo(a: u8) S {
return .{ .fnPtr = bar, .a = a };
fn foo(comptime a: *u8) S {
return .{ .fnPtr = bar, .a = a.* };
}
pub export fn entry() void {
var a: u8 = 1;
_ = foo(a);
_ = foo(&a);
}
// error
@ -18,6 +18,6 @@ pub export fn entry() void {
//
// :12:13: error: unable to resolve comptime value
// :12:13: note: argument to function being called at comptime must be comptime known
// :7:15: note: function is being called at comptime because it returns a comptime only type 'tmp.S'
// :7:25: note: function is being called at comptime because it returns a comptime only type 'tmp.S'
// :2:12: note: struct requires comptime because of this field
// :2:12: note: use '*const fn() void' for a function pointer type

View File

@ -0,0 +1,36 @@
fn F(val: anytype) type {
_ = val;
return struct {};
}
export fn entry() void {
_ = F(void{});
}
const S = struct {
foo: fn () void,
};
fn bar(_: u32) S {
return undefined;
}
export fn entry1() void {
_ = bar();
}
// prioritize other return type errors
fn foo(a: u32) callconv(.C) comptime_int {
return a;
}
export fn entry2() void {
_ = foo(1);
}
// error
// backend=stage2
// target=native
//
// :1:20: error: function with comptime only return type 'type' requires all parameters to be comptime
// :1:20: note: types are not available at runtime
// :1:6: note: param 'val' is required to be comptime
// :11:16: error: function with comptime only return type 'tmp.S' requires all parameters to be comptime
// :9:10: note: struct requires comptime because of this field
// :9:10: note: use '*const fn() void' for a function pointer type
// :11:8: note: param is required to be comptime
// :18:29: error: return type 'comptime_int' not allowed in function with calling convention 'C'