stage2: LLVM backend: implement @tagName for enums

Introduced a new AIR instruction: `tag_name`. Reasons to do this
instead of lowering it in Sema to a switch, function call, array
lookup, or if-else tower:
 * Sema is a bottleneck; do less work in Sema whenever possible.
 * If any optimization passes run, and the operand to becomes
   comptime-known, then it could change to have a comptime result
   value instead of lowering to a function or array or something which
   would then have to be garbage-collected.
 * Backends may want to choose to use a function and a switch branch,
   or they may want to use a different strategy.

Codegen for `@tagName` is implemented for the LLVM backend but not any
others yet.

Introduced some new `Type` tags:
 * `const_slice_u8_sentinel_0`
 * `manyptr_const_u8_sentinel_0`

The motivation for this was to make typeof() on the tag_name AIR
instruction non-allocating.

A bunch more enum tests are passing now.
This commit is contained in:
Andrew Kelley
2021-12-27 01:14:50 -07:00
parent f41b9cdb6d
commit c8fb36b36c
17 changed files with 1031 additions and 654 deletions

View File

@@ -699,3 +699,126 @@ test "single field non-exhaustive enum" {
try S.doTheTest(23);
comptime try S.doTheTest(23);
}
const EnumWithTagValues = enum(u4) {
A = 1 << 0,
B = 1 << 1,
C = 1 << 2,
D = 1 << 3,
};
test "enum with tag values don't require parens" {
try expect(@enumToInt(EnumWithTagValues.C) == 0b0100);
}
const MultipleChoice2 = enum(u32) {
Unspecified1,
A = 20,
Unspecified2,
B = 40,
Unspecified3,
C = 60,
Unspecified4,
D = 1000,
Unspecified5,
};
test "cast integer literal to enum" {
try expect(@intToEnum(MultipleChoice2, 0) == MultipleChoice2.Unspecified1);
try expect(@intToEnum(MultipleChoice2, 40) == MultipleChoice2.B);
}
test "enum with specified and unspecified tag values" {
try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D);
comptime try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D);
}
fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) !void {
try expect(@enumToInt(x) == 1000);
try expect(1234 == switch (x) {
MultipleChoice2.A => 1,
MultipleChoice2.B => 2,
MultipleChoice2.C => 3,
MultipleChoice2.D => @as(u32, 1234),
MultipleChoice2.Unspecified1 => 5,
MultipleChoice2.Unspecified2 => 6,
MultipleChoice2.Unspecified3 => 7,
MultipleChoice2.Unspecified4 => 8,
MultipleChoice2.Unspecified5 => 9,
});
}
const Small2 = enum(u2) { One, Two };
const Small = enum(u2) { One, Two, Three, Four };
test "set enum tag type" {
{
var x = Small.One;
x = Small.Two;
comptime try expect(Tag(Small) == u2);
}
{
var x = Small2.One;
x = Small2.Two;
comptime try expect(Tag(Small2) == u2);
}
}
test "casting enum to its tag type" {
try testCastEnumTag(Small2.Two);
comptime try testCastEnumTag(Small2.Two);
}
fn testCastEnumTag(value: Small2) !void {
try expect(@enumToInt(value) == 1);
}
test "enum with 1 field but explicit tag type should still have the tag type" {
const Enum = enum(u8) {
B = 2,
};
comptime try expect(@sizeOf(Enum) == @sizeOf(u8));
}
test "signed integer as enum tag" {
const SignedEnum = enum(i2) {
A0 = -1,
A1 = 0,
A2 = 1,
};
try expect(@enumToInt(SignedEnum.A0) == -1);
try expect(@enumToInt(SignedEnum.A1) == 0);
try expect(@enumToInt(SignedEnum.A2) == 1);
}
test "enum with one member and custom tag type" {
const E = enum(u2) {
One,
};
try expect(@enumToInt(E.One) == 0);
const E2 = enum(u2) {
One = 2,
};
try expect(@enumToInt(E2.One) == 2);
}
test "enum with one member and u1 tag type @enumToInt" {
const Enum = enum(u1) {
Test,
};
try expect(@enumToInt(Enum.Test) == 0);
}
test "enum with comptime_int tag type" {
const Enum = enum(comptime_int) {
One = 3,
Two = 2,
Three = 1,
};
comptime try expect(Tag(Enum) == comptime_int);
}
test "enum with one member default to u0 tag type" {
const E0 = enum { X };
comptime try expect(Tag(E0) == u0);
}