IR: pass enumToInt test
parent
1b5d1877ae
commit
4664f793dc
|
@ -429,14 +429,12 @@ enum CastOp {
|
||||||
CastOpNoop, // fn call expr is a cast, but does nothing
|
CastOpNoop, // fn call expr is a cast, but does nothing
|
||||||
CastOpPtrToInt,
|
CastOpPtrToInt,
|
||||||
CastOpIntToPtr,
|
CastOpIntToPtr,
|
||||||
CastOpWidenOrShorten,
|
|
||||||
CastOpErrToInt,
|
CastOpErrToInt,
|
||||||
CastOpIntToFloat,
|
CastOpIntToFloat,
|
||||||
CastOpFloatToInt,
|
CastOpFloatToInt,
|
||||||
CastOpBoolToInt,
|
CastOpBoolToInt,
|
||||||
CastOpResizeSlice,
|
CastOpResizeSlice,
|
||||||
CastOpIntToEnum,
|
CastOpIntToEnum,
|
||||||
CastOpEnumToInt,
|
|
||||||
CastOpBytesToSlice,
|
CastOpBytesToSlice,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1454,6 +1452,7 @@ enum IrInstructionId {
|
||||||
IrInstructionIdTestComptime,
|
IrInstructionIdTestComptime,
|
||||||
IrInstructionIdInitEnum,
|
IrInstructionIdInitEnum,
|
||||||
IrInstructionIdPointerReinterpret,
|
IrInstructionIdPointerReinterpret,
|
||||||
|
IrInstructionIdWidenOrShorten,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct IrInstruction {
|
struct IrInstruction {
|
||||||
|
@ -2096,6 +2095,12 @@ struct IrInstructionPointerReinterpret {
|
||||||
IrInstruction *ptr;
|
IrInstruction *ptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct IrInstructionWidenOrShorten {
|
||||||
|
IrInstruction base;
|
||||||
|
|
||||||
|
IrInstruction *target;
|
||||||
|
};
|
||||||
|
|
||||||
enum LValPurpose {
|
enum LValPurpose {
|
||||||
LValPurposeNone,
|
LValPurposeNone,
|
||||||
LValPurposeAssign,
|
LValPurposeAssign,
|
||||||
|
|
|
@ -973,9 +973,6 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,
|
||||||
return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, "");
|
return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, "");
|
||||||
case CastOpIntToPtr:
|
case CastOpIntToPtr:
|
||||||
return LLVMBuildIntToPtr(g->builder, expr_val, wanted_type->type_ref, "");
|
return LLVMBuildIntToPtr(g->builder, expr_val, wanted_type->type_ref, "");
|
||||||
case CastOpWidenOrShorten:
|
|
||||||
return gen_widen_or_shorten(g, ir_want_debug_safety(g, &cast_instruction->base),
|
|
||||||
actual_type, wanted_type, expr_val);
|
|
||||||
case CastOpResizeSlice:
|
case CastOpResizeSlice:
|
||||||
{
|
{
|
||||||
assert(cast_instruction->tmp_ptr);
|
assert(cast_instruction->tmp_ptr);
|
||||||
|
@ -1086,9 +1083,6 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,
|
||||||
case CastOpIntToEnum:
|
case CastOpIntToEnum:
|
||||||
return gen_widen_or_shorten(g, ir_want_debug_safety(g, &cast_instruction->base),
|
return gen_widen_or_shorten(g, ir_want_debug_safety(g, &cast_instruction->base),
|
||||||
actual_type, wanted_type->data.enumeration.tag_type, expr_val);
|
actual_type, wanted_type->data.enumeration.tag_type, expr_val);
|
||||||
case CastOpEnumToInt:
|
|
||||||
return gen_widen_or_shorten(g, ir_want_debug_safety(g, &cast_instruction->base),
|
|
||||||
actual_type->data.enumeration.tag_type, wanted_type, expr_val);
|
|
||||||
}
|
}
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
}
|
}
|
||||||
|
@ -1101,6 +1095,24 @@ static LLVMValueRef ir_render_pointer_reinterpret(CodeGen *g, IrExecutable *exec
|
||||||
return LLVMBuildBitCast(g->builder, ptr, wanted_type->type_ref, "");
|
return LLVMBuildBitCast(g->builder, ptr, wanted_type->type_ref, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static LLVMValueRef ir_render_widen_or_shorten(CodeGen *g, IrExecutable *executable,
|
||||||
|
IrInstructionWidenOrShorten *instruction)
|
||||||
|
{
|
||||||
|
TypeTableEntry *actual_type = instruction->target->value.type;
|
||||||
|
// TODO instead of this logic, use the Noop instruction to change the type from
|
||||||
|
// enum_tag to the underlying int type
|
||||||
|
TypeTableEntry *int_type;
|
||||||
|
if (actual_type->id == TypeTableEntryIdEnum) {
|
||||||
|
TypeTableEntry *tag_type = actual_type->data.enumeration.tag_type;
|
||||||
|
assert(tag_type->id == TypeTableEntryIdEnumTag);
|
||||||
|
int_type = tag_type->data.enum_tag.int_type;
|
||||||
|
} else {
|
||||||
|
int_type = actual_type;
|
||||||
|
}
|
||||||
|
LLVMValueRef target_val = ir_llvm_value(g, instruction->target);
|
||||||
|
return gen_widen_or_shorten(g, ir_want_debug_safety(g, &instruction->base), int_type,
|
||||||
|
instruction->base.value.type, target_val);
|
||||||
|
}
|
||||||
|
|
||||||
static LLVMValueRef ir_render_unreachable(CodeGen *g, IrExecutable *executable,
|
static LLVMValueRef ir_render_unreachable(CodeGen *g, IrExecutable *executable,
|
||||||
IrInstructionUnreachable *unreachable_instruction)
|
IrInstructionUnreachable *unreachable_instruction)
|
||||||
|
@ -2309,6 +2321,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
||||||
return ir_render_struct_init(g, executable, (IrInstructionStructInit *)instruction);
|
return ir_render_struct_init(g, executable, (IrInstructionStructInit *)instruction);
|
||||||
case IrInstructionIdPointerReinterpret:
|
case IrInstructionIdPointerReinterpret:
|
||||||
return ir_render_pointer_reinterpret(g, executable, (IrInstructionPointerReinterpret *)instruction);
|
return ir_render_pointer_reinterpret(g, executable, (IrInstructionPointerReinterpret *)instruction);
|
||||||
|
case IrInstructionIdWidenOrShorten:
|
||||||
|
return ir_render_widen_or_shorten(g, executable, (IrInstructionWidenOrShorten *)instruction);
|
||||||
case IrInstructionIdSwitchVar:
|
case IrInstructionIdSwitchVar:
|
||||||
zig_panic("TODO render switch var instruction to LLVM");
|
zig_panic("TODO render switch var instruction to LLVM");
|
||||||
case IrInstructionIdContainerInitList:
|
case IrInstructionIdContainerInitList:
|
||||||
|
|
71
src/ir.cpp
71
src/ir.cpp
|
@ -455,6 +455,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionPointerReinterpr
|
||||||
return IrInstructionIdPointerReinterpret;
|
return IrInstructionIdPointerReinterpret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionWidenOrShorten *) {
|
||||||
|
return IrInstructionIdWidenOrShorten;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static T *ir_create_instruction(IrExecutable *exec, Scope *scope, AstNode *source_node) {
|
static T *ir_create_instruction(IrExecutable *exec, Scope *scope, AstNode *source_node) {
|
||||||
T *special_instruction = allocate<T>(1);
|
T *special_instruction = allocate<T>(1);
|
||||||
|
@ -1883,6 +1887,18 @@ static IrInstruction *ir_build_pointer_reinterpret(IrBuilder *irb, Scope *scope,
|
||||||
return &instruction->base;
|
return &instruction->base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static IrInstruction *ir_build_widen_or_shorten(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||||
|
IrInstruction *target)
|
||||||
|
{
|
||||||
|
IrInstructionWidenOrShorten *instruction = ir_build_instruction<IrInstructionWidenOrShorten>(
|
||||||
|
irb, scope, source_node);
|
||||||
|
instruction->target = target;
|
||||||
|
|
||||||
|
ir_ref_instruction(target);
|
||||||
|
|
||||||
|
return &instruction->base;
|
||||||
|
}
|
||||||
|
|
||||||
static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
|
static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
|
||||||
results[ReturnKindUnconditional] = 0;
|
results[ReturnKindUnconditional] = 0;
|
||||||
results[ReturnKindError] = 0;
|
results[ReturnKindError] = 0;
|
||||||
|
@ -4638,7 +4654,6 @@ static void eval_const_expr_implicit_cast(CastOp cast_op,
|
||||||
case CastOpNoCast:
|
case CastOpNoCast:
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
case CastOpNoop:
|
case CastOpNoop:
|
||||||
case CastOpWidenOrShorten:
|
|
||||||
*const_val = *other_val;
|
*const_val = *other_val;
|
||||||
const_val->type = new_type;
|
const_val->type = new_type;
|
||||||
break;
|
break;
|
||||||
|
@ -4684,10 +4699,6 @@ static void eval_const_expr_implicit_cast(CastOp cast_op,
|
||||||
const_val->special = ConstValSpecialStatic;
|
const_val->special = ConstValSpecialStatic;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CastOpEnumToInt:
|
|
||||||
bignum_init_unsigned(&const_val->data.x_bignum, other_val->data.x_enum.tag);
|
|
||||||
const_val->special = ConstValSpecialStatic;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
|
static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
|
||||||
|
@ -5181,6 +5192,50 @@ static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *s
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *source_instr,
|
||||||
|
IrInstruction *target, TypeTableEntry *wanted_type)
|
||||||
|
{
|
||||||
|
assert(wanted_type->id == TypeTableEntryIdInt);
|
||||||
|
|
||||||
|
if (instr_is_comptime(target)) {
|
||||||
|
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
|
||||||
|
if (!val)
|
||||||
|
return ira->codegen->invalid_instruction;
|
||||||
|
IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
|
||||||
|
source_instr->source_node, wanted_type, val->depends_on_compile_var);
|
||||||
|
init_const_unsigned_negative(&result->value, wanted_type, val->data.x_enum.tag, false);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
IrInstruction *result = ir_build_widen_or_shorten(&ira->new_irb, source_instr->scope,
|
||||||
|
source_instr->source_node, target);
|
||||||
|
result->value.type = wanted_type;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction *source_instr,
|
||||||
|
IrInstruction *target, TypeTableEntry *wanted_type)
|
||||||
|
{
|
||||||
|
assert(wanted_type->id == TypeTableEntryIdInt || wanted_type->id == TypeTableEntryIdFloat);
|
||||||
|
|
||||||
|
if (instr_is_comptime(target)) {
|
||||||
|
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
|
||||||
|
if (!val)
|
||||||
|
return ira->codegen->invalid_instruction;
|
||||||
|
IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
|
||||||
|
source_instr->source_node, wanted_type, val->depends_on_compile_var);
|
||||||
|
result->value = *val;
|
||||||
|
result->value.type = wanted_type;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
IrInstruction *result = ir_build_widen_or_shorten(&ira->new_irb, source_instr->scope,
|
||||||
|
source_instr->source_node, target);
|
||||||
|
result->value.type = wanted_type;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr,
|
static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr,
|
||||||
TypeTableEntry *wanted_type, IrInstruction *value)
|
TypeTableEntry *wanted_type, IrInstruction *value)
|
||||||
{
|
{
|
||||||
|
@ -5233,7 +5288,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
|
||||||
(wanted_type_canon->id == TypeTableEntryIdFloat &&
|
(wanted_type_canon->id == TypeTableEntryIdFloat &&
|
||||||
actual_type_canon->id == TypeTableEntryIdFloat))
|
actual_type_canon->id == TypeTableEntryIdFloat))
|
||||||
{
|
{
|
||||||
return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpWidenOrShorten, false);
|
return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
// explicit cast from int to float
|
// explicit cast from int to float
|
||||||
|
@ -5411,7 +5466,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
|
||||||
actual_type->id == TypeTableEntryIdEnum &&
|
actual_type->id == TypeTableEntryIdEnum &&
|
||||||
actual_type->data.enumeration.gen_field_count == 0)
|
actual_type->data.enumeration.gen_field_count == 0)
|
||||||
{
|
{
|
||||||
return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpEnumToInt, false);
|
return ir_analyze_enum_to_int(ira, source_instr, value, wanted_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
// explicit cast from undefined to anything
|
// explicit cast from undefined to anything
|
||||||
|
@ -9882,6 +9937,7 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
|
||||||
switch (instruction->id) {
|
switch (instruction->id) {
|
||||||
case IrInstructionIdInvalid:
|
case IrInstructionIdInvalid:
|
||||||
case IrInstructionIdPointerReinterpret:
|
case IrInstructionIdPointerReinterpret:
|
||||||
|
case IrInstructionIdWidenOrShorten:
|
||||||
case IrInstructionIdStructInit:
|
case IrInstructionIdStructInit:
|
||||||
case IrInstructionIdStructFieldPtr:
|
case IrInstructionIdStructFieldPtr:
|
||||||
case IrInstructionIdEnumFieldPtr:
|
case IrInstructionIdEnumFieldPtr:
|
||||||
|
@ -10188,6 +10244,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
||||||
case IrInstructionIdTestComptime:
|
case IrInstructionIdTestComptime:
|
||||||
case IrInstructionIdInitEnum:
|
case IrInstructionIdInitEnum:
|
||||||
case IrInstructionIdPointerReinterpret:
|
case IrInstructionIdPointerReinterpret:
|
||||||
|
case IrInstructionIdWidenOrShorten:
|
||||||
return false;
|
return false;
|
||||||
case IrInstructionIdAsm:
|
case IrInstructionIdAsm:
|
||||||
{
|
{
|
||||||
|
|
|
@ -923,11 +923,17 @@ static void ir_print_init_enum(IrPrint *irp, IrInstructionInitEnum *instruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ir_print_pointer_reinterpret(IrPrint *irp, IrInstructionPointerReinterpret *instruction) {
|
static void ir_print_pointer_reinterpret(IrPrint *irp, IrInstructionPointerReinterpret *instruction) {
|
||||||
fprintf(irp->f, "(%s)(", buf_ptr(&instruction->base.value.type->name));
|
fprintf(irp->f, "@pointerReinterpret(");
|
||||||
ir_print_other_instruction(irp, instruction->ptr);
|
ir_print_other_instruction(irp, instruction->ptr);
|
||||||
fprintf(irp->f, ")");
|
fprintf(irp->f, ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ir_print_widen_or_shorten(IrPrint *irp, IrInstructionWidenOrShorten *instruction) {
|
||||||
|
fprintf(irp->f, "@widenOrShorten(");
|
||||||
|
ir_print_other_instruction(irp, instruction->target);
|
||||||
|
fprintf(irp->f, ")");
|
||||||
|
}
|
||||||
|
|
||||||
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||||
ir_print_prefix(irp, instruction);
|
ir_print_prefix(irp, instruction);
|
||||||
switch (instruction->id) {
|
switch (instruction->id) {
|
||||||
|
@ -1170,6 +1176,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||||
case IrInstructionIdPointerReinterpret:
|
case IrInstructionIdPointerReinterpret:
|
||||||
ir_print_pointer_reinterpret(irp, (IrInstructionPointerReinterpret *)instruction);
|
ir_print_pointer_reinterpret(irp, (IrInstructionPointerReinterpret *)instruction);
|
||||||
break;
|
break;
|
||||||
|
case IrInstructionIdWidenOrShorten:
|
||||||
|
ir_print_widen_or_shorten(irp, (IrInstructionWidenOrShorten *)instruction);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
fprintf(irp->f, "\n");
|
fprintf(irp->f, "\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
const assert = @import("std").debug.assert;
|
|
||||||
|
|
||||||
enum Number {
|
|
||||||
Zero,
|
|
||||||
One,
|
|
||||||
Two,
|
|
||||||
Three,
|
|
||||||
Four,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enumToInt() {
|
|
||||||
@setFnTest(this, true);
|
|
||||||
|
|
||||||
shouldEqual(false, Number.Zero, 0);
|
|
||||||
shouldEqual(false, Number.One, 1);
|
|
||||||
shouldEqual(false, Number.Two, 2);
|
|
||||||
shouldEqual(false, Number.Three, 3);
|
|
||||||
shouldEqual(false, Number.Four, 4);
|
|
||||||
|
|
||||||
shouldEqual(true, Number.Zero, 0);
|
|
||||||
shouldEqual(true, Number.One, 1);
|
|
||||||
shouldEqual(true, Number.Two, 2);
|
|
||||||
shouldEqual(true, Number.Three, 3);
|
|
||||||
shouldEqual(true, Number.Four, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn shouldEqual(inline static_eval: bool, n: Number, expected: usize) {
|
|
||||||
@setFnStaticEval(this, static_eval);
|
|
||||||
|
|
||||||
assert(usize(n) == expected);
|
|
||||||
}
|
|
|
@ -73,6 +73,29 @@ const AnEnumWithPayload = enum {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const Number = enum {
|
||||||
|
Zero,
|
||||||
|
One,
|
||||||
|
Two,
|
||||||
|
Three,
|
||||||
|
Four,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn enumToInt() {
|
||||||
|
@setFnTest(this);
|
||||||
|
|
||||||
|
shouldEqual(Number.Zero, 0);
|
||||||
|
shouldEqual(Number.One, 1);
|
||||||
|
shouldEqual(Number.Two, 2);
|
||||||
|
shouldEqual(Number.Three, 3);
|
||||||
|
shouldEqual(Number.Four, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shouldEqual(n: Number, expected: usize) {
|
||||||
|
assert(usize(n) == expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO import from std
|
||||||
fn assert(ok: bool) {
|
fn assert(ok: bool) {
|
||||||
if (!ok)
|
if (!ok)
|
||||||
@unreachable();
|
@unreachable();
|
||||||
|
|
Loading…
Reference in New Issue