Sema: fix anytype parameters whose types require comptime

This commit is contained in:
Andrew Kelley 2021-12-27 19:39:28 -07:00
parent fc1a5cd9e7
commit 6ed7850972
7 changed files with 103 additions and 98 deletions

View File

@ -4187,10 +4187,17 @@ fn analyzeCall(
return sema.failWithNeededComptime(block, arg_src);
}
} else if (is_anytype) {
// We insert into the map an instruction which is runtime-known
// but has the type of the argument.
const child_arg = try child_block.addArg(sema.typeOf(arg), 0);
child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
const arg_ty = sema.typeOf(arg);
if (arg_ty.requiresComptime()) {
const arg_val = try sema.resolveConstValue(block, arg_src, arg);
const child_arg = try child_sema.addConstant(arg_ty, arg_val);
child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
} else {
// We insert into the map an instruction which is runtime-known
// but has the type of the argument.
const child_arg = try child_block.addArg(arg_ty, 0);
child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
}
}
arg_i += 1;
}
@ -5130,9 +5137,8 @@ fn funcCommon(
const comptime_params = try sema.arena.alloc(bool, block.params.items.len);
for (block.params.items) |param, i| {
param_types[i] = param.ty;
comptime_params[i] = param.is_comptime;
is_generic = is_generic or param.is_comptime or
param.ty.tag() == .generic_poison or param.ty.requiresComptime();
comptime_params[i] = param.is_comptime or param.ty.requiresComptime();
is_generic = is_generic or comptime_params[i] or param.ty.tag() == .generic_poison;
}
if (align_val.tag() != .null_value) {
@ -13146,7 +13152,7 @@ fn coerceInMemoryAllowedFns(
return .no_match;
}
// TODO: nolias
// TODO: noalias
// Note: Cast direction is reversed here.
const param = try sema.coerceInMemoryAllowed(block, src_param_ty, dest_param_ty, false, target, dest_src, src_src);

View File

@ -4275,7 +4275,6 @@ pub const Type = extern union {
is_generic: bool,
pub fn paramIsComptime(self: @This(), i: usize) bool {
if (!self.is_generic) return false;
assert(i < self.param_types.len);
return self.comptime_params[i];
}

View File

@ -67,6 +67,7 @@ test {
_ = @import("behavior/bugs/394.zig");
_ = @import("behavior/bugs/656.zig");
_ = @import("behavior/bugs/1277.zig");
_ = @import("behavior/bugs/1310.zig");
_ = @import("behavior/bugs/1381.zig");
_ = @import("behavior/bugs/1500.zig");
_ = @import("behavior/bugs/1741.zig");
@ -74,7 +75,9 @@ test {
_ = @import("behavior/bugs/2578.zig");
_ = @import("behavior/bugs/3007.zig");
_ = @import("behavior/bugs/3112.zig");
_ = @import("behavior/bugs/3367.zig");
_ = @import("behavior/bugs/7250.zig");
_ = @import("behavior/bugs/9584.zig");
_ = @import("behavior/cast_llvm.zig");
_ = @import("behavior/enum_llvm.zig");
_ = @import("behavior/eval.zig");
@ -121,7 +124,6 @@ test {
_ = @import("behavior/bugs/1025.zig");
_ = @import("behavior/bugs/1076.zig");
_ = @import("behavior/bugs/1120.zig");
_ = @import("behavior/bugs/1310.zig");
_ = @import("behavior/bugs/1322.zig");
_ = @import("behavior/bugs/1421.zig");
_ = @import("behavior/bugs/1442.zig");
@ -130,7 +132,6 @@ test {
_ = @import("behavior/bugs/1851.zig");
_ = @import("behavior/bugs/1914.zig");
_ = @import("behavior/bugs/2114.zig");
_ = @import("behavior/bugs/3367.zig");
_ = @import("behavior/bugs/3384.zig");
_ = @import("behavior/bugs/3742.zig");
_ = @import("behavior/bugs/3779.zig");
@ -144,7 +145,6 @@ test {
_ = @import("behavior/bugs/7003.zig");
_ = @import("behavior/bugs/7027.zig");
_ = @import("behavior/bugs/7047.zig");
_ = @import("behavior/bugs/9584.zig");
_ = @import("behavior/bugs/10147.zig");
_ = @import("behavior/byteswap.zig");
_ = @import("behavior/call_stage1.zig");

View File

@ -822,3 +822,51 @@ test "enum with one member default to u0 tag type" {
const E0 = enum { X };
comptime try expect(Tag(E0) == u0);
}
const EnumWithOneMember = enum { Eof };
fn doALoopThing(id: EnumWithOneMember) void {
while (true) {
if (id == EnumWithOneMember.Eof) {
break;
}
@compileError("above if condition should be comptime");
}
}
test "comparison operator on enum with one member is comptime known" {
doALoopThing(EnumWithOneMember.Eof);
}
const State = enum { Start };
test "switch on enum with one member is comptime known" {
var state = State.Start;
switch (state) {
State.Start => return,
}
@compileError("analysis should not reach here");
}
test "method call on an enum" {
const S = struct {
const E = enum {
one,
two,
fn method(self: *E) bool {
return self.* == .two;
}
fn generic_method(self: *E, foo: anytype) bool {
return self.* == .two and foo == bool;
}
};
fn doTheTest() !void {
var e = E.two;
try expect(e.method());
try expect(e.generic_method(bool));
}
};
try S.doTheTest();
comptime try S.doTheTest();
}

View File

@ -96,30 +96,6 @@ fn getC(data: *const BitFieldOfEnums) C {
return data.c;
}
const EnumWithOneMember = enum { Eof };
fn doALoopThing(id: EnumWithOneMember) void {
while (true) {
if (id == EnumWithOneMember.Eof) {
break;
}
@compileError("above if condition should be comptime");
}
}
test "comparison operator on enum with one member is comptime known" {
doALoopThing(EnumWithOneMember.Eof);
}
const State = enum { Start };
test "switch on enum with one member is comptime known" {
var state = State.Start;
switch (state) {
State.Start => return,
}
@compileError("analysis should not reach here");
}
test "enum literal in array literal" {
const Items = enum { one, two };
const array = [_]Items{ .one, .two };

View File

@ -43,30 +43,6 @@ test "enum literal casting to error union with payload enum" {
try expect((try bar) == Bar.B);
}
test "method call on an enum" {
const S = struct {
const E = enum {
one,
two,
fn method(self: *E) bool {
return self.* == .two;
}
fn generic_method(self: *E, foo: anytype) bool {
return self.* == .two and foo == bool;
}
};
fn doTheTest() !void {
var e = E.two;
try expect(e.method());
try expect(e.generic_method(bool));
}
};
try S.doTheTest();
comptime try S.doTheTest();
}
test "exporting enum type and value" {
const S = struct {
const E = enum(c_int) { one, two };
@ -80,3 +56,41 @@ test "exporting enum type and value" {
};
try expect(S.e == .two);
}
test "constant enum initialization with differing sizes" {
try test3_1(test3_foo);
try test3_2(test3_bar);
}
const Test3Foo = union(enum) {
One: void,
Two: f32,
Three: Test3Point,
};
const Test3Point = struct {
x: i32,
y: i32,
};
const test3_foo = Test3Foo{
.Three = Test3Point{
.x = 3,
.y = 4,
},
};
const test3_bar = Test3Foo{ .Two = 13 };
fn test3_1(f: Test3Foo) !void {
switch (f) {
Test3Foo.Three => |pt| {
try expect(pt.x == 3);
try expect(pt.y == 4);
},
else => unreachable,
}
}
fn test3_2(f: Test3Foo) !void {
switch (f) {
Test3Foo.Two => |x| {
try expect(x == 13);
},
else => unreachable,
}
}

View File

@ -34,44 +34,6 @@ test "explicit cast optional pointers" {
_ = b;
}
test "constant enum initialization with differing sizes" {
try test3_1(test3_foo);
try test3_2(test3_bar);
}
const Test3Foo = union(enum) {
One: void,
Two: f32,
Three: Test3Point,
};
const Test3Point = struct {
x: i32,
y: i32,
};
const test3_foo = Test3Foo{
.Three = Test3Point{
.x = 3,
.y = 4,
},
};
const test3_bar = Test3Foo{ .Two = 13 };
fn test3_1(f: Test3Foo) !void {
switch (f) {
Test3Foo.Three => |pt| {
try expect(pt.x == 3);
try expect(pt.y == 4);
},
else => unreachable,
}
}
fn test3_2(f: Test3Foo) !void {
switch (f) {
Test3Foo.Two => |x| {
try expect(x == 13);
},
else => unreachable,
}
}
test "pointer comparison" {
const a = @as([]const u8, "a");
const b = &a;