Skip to content
Open
71 changes: 36 additions & 35 deletions src/checker.c
Original file line number Diff line number Diff line change
Expand Up @@ -223,12 +223,16 @@ b32 IsFloat(Type *type) {
return type->kind == TypeKind_Float;
}

b32 isEnum(Type *type) {
return type->kind == TypeKind_Enum;
}

b32 isFunction(Type *type) {
return type->kind == TypeKind_Function;
}

b32 isNumeric(Type *type) {
return IsInteger(type) || IsFloat(type);
return IsInteger(type) || IsFloat(type) || isEnum(type);
}

b32 isBoolean(Type *type) {
Expand Down Expand Up @@ -267,10 +271,6 @@ b32 isSlice(Type *type) {
return type->kind == TypeKind_Slice;
}

b32 isEnum(Type *type) {
return type->kind == TypeKind_Enum;
}

b32 isEnumFlags(Type *type) {
return isEnum(type) && (type->Flags & TypeFlag_EnumFlags) != 0;
}
Expand Down Expand Up @@ -366,7 +366,7 @@ b32 canCoerce(Type *type, Type *target, CheckerContext *ctx) {
return type->Width <= target->Width;
}

if (type->kind == TypeKind_Enum || IsInteger(target)) {
if (isEnum(type) && IsInteger(target)) {
return canCoerce(type->Enum.backingType, target, ctx);
}

Expand Down Expand Up @@ -474,6 +474,10 @@ Conversion conversion(Type *type, Type *target) {
return result;
}

if (type->kind == TypeKind_Enum && IsInteger(target)) {
return ConversionKind_None;
}

if (IsFloat(type) && IsInteger(target)) {
result |= ConversionKind_FtoI & ConversionFlag_Float;
if (IsSigned(type)) result |= ConversionFlag_Signed;
Expand Down Expand Up @@ -872,31 +876,28 @@ Type *checkExprTypeEnum(Expr *expr, CheckerContext *ctx, Package *pkg) {
ASSERT(expr->kind == ExprKind_TypeEnum);
Expr_TypeEnum enm = expr->TypeEnum;

b32 hasMinMax = false;
u64 maxValue;

Type *backingType = NULL;
if (enm.explicitType) {
Type *type = checkExpr(enm.explicitType, ctx, pkg);
expectType(pkg, type, ctx, enm.explicitType->start);
if (IsInteger(type)) {
backingType = type;
} else {
ReportError(
pkg, TypeMismatchError, enm.explicitType->start,
"Enum backing type must be an integer. Got: %s",
DescribeType(type)
);
if (ctx->mode == ExprMode_Unresolved) goto unresolved;

if (ctx->mode != ExprMode_Invalid) {
expectType(pkg, type, ctx, enm.explicitType->start);
if (IsInteger(type)) {
// TODO: flags
maxValue = MaxValueForIntOrPointerType(type);
hasMinMax = true;
backingType = type;
} else {
ReportError(pkg, TypeMismatchError, enm.explicitType->start,
"Enum backing type must be an integer. Got: %s", DescribeType(type));
}
}
}

b32 firstValue = true;
b32 hasMinMax = false;
u64 maxValue;

if (backingType) {
// TODO: flags
maxValue = MaxValueForIntOrPointerType(backingType);
hasMinMax = true;
}

DynamicArray(EnumField) fields = NULL;
ArrayFit(fields, ArrayLen(enm.items));

Expand All @@ -909,20 +910,15 @@ Type *checkExprTypeEnum(Expr *expr, CheckerContext *ctx, Package *pkg) {
if (item.init) {
CheckerContext itemCtx = {.scope = ctx->scope, .desiredType = backingType};
Type *type = checkExpr(item.init, &itemCtx, pkg);
if (itemCtx.mode == ExprMode_Unresolved) goto unresolved; // TODO: @Leak this will leak the 'fields' array

if (!IsConstant(&itemCtx)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a b32 expectType(Package *pkg, Type *type, CheckerContext *ctx, Position pos) do you think we should have a b32 expectConstant(Package *pkg, CheckerContext *ctx, Position pos)? This would mean we would get a more consistent error message. What do you think?

ReportError(
pkg, TODOError, item.init->start,
ReportError(pkg, TODOError, item.init->start,
"Enum cases must be a constant value");
continue;
}

if (backingType && !canCoerce(type, backingType, &itemCtx)) {
ReportError(
pkg, InvalidConversionError, item.init->start,
"Cannot convert %s to type %s",
DescribeExpr(item.init), DescribeType(backingType)
);
if (backingType && !coerceType(item.init, ctx, &type, backingType, pkg)) {
continue;
}

Expand All @@ -932,7 +928,8 @@ Type *checkExprTypeEnum(Expr *expr, CheckerContext *ctx, Package *pkg) {
}

if (hasMinMax && currentValue > maxValue) {
printf("oops!\n");
ReportError(pkg, IntOverflowError, item.init->start,
"Enum case is will overflow backing type");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReportError(pkg, IntOverflowError, item.init->start,
            "Value for enum case exceeds the max value for the enum backing type (%s)", 
            DescribeType(backingType));
ReportNote(pkg, item.init->start, 
           "You can force the overflow by explicitly casting the value '%s(%s)" 
           DescribeType(backingType), DescribeExpr(item.init));

continue;
}

Expand All @@ -945,6 +942,10 @@ Type *checkExprTypeEnum(Expr *expr, CheckerContext *ctx, Package *pkg) {
storeInfoBasicExpr(pkg, expr, type, ctx);
ctx->mode = ExprMode_Type;
return type;

unresolved:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix the prior leak of fields with:

if (fields) ArrayFree(fields);

ctx->mode = ExprMode_Unresolved;
return NULL;
}

Type *checkExprTypeFunction(Expr *expr, CheckerContext *ctx, Package *pkg) {
Expand Down Expand Up @@ -1670,7 +1671,7 @@ Type *checkExprSelector(Expr *expr, CheckerContext *ctx, Package *pkg) {
}

ctx->val.u64 = result.field->val;
SelectorValue val = {.Enum.index = result.index};
SelectorValue val = {.Enum.value = result.field->val};
storeInfoSelector(pkg, expr, base, SelectorKind_Enum, val, ctx);
type = base;
ctx->mode = ExprMode_Addressable;
Expand Down
2 changes: 1 addition & 1 deletion src/checker.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ struct Selector_Struct {

typedef struct Selector_Enum Selector_Enum;
struct Selector_Enum {
u32 index;
u64 value;
};

typedef struct Selector_Import Selector_Import;
Expand Down
125 changes: 96 additions & 29 deletions src/llvm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,15 @@ llvm::Value *emitExprSelector(Context *ctx, Expr *expr) {
return ctx->b.CreateAlignedLoad(addr, BytesFromBits(info.value.Struct.index));
}

case SelectorKind_Enum: {
llvm::Type *type = canonicalize(ctx, info.type);
if (ctx->returnAddress) {
UNIMPLEMENTED();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The frontend should report errors if a user tries to address an enum. This should be an ASSERT(false)

}

return llvm::ConstantInt::get(type, info.value.Enum.value);
}

case SelectorKind_Import: {
Symbol *symbol = info.value.Import.symbol;
if (!symbol->backendUserdata) {
Expand Down Expand Up @@ -1006,6 +1015,63 @@ llvm::Function *emitExprLitFunction(Context *ctx, Expr *expr, llvm::Function *fn
return fn;
}

llvm::Type *emitExprTypeEnum(Context *ctx, Expr *expr) {
ASSERT(expr->kind == ExprKind_TypeEnum);

Type *type = TypeFromCheckerInfo(ctx->checkerInfo[expr->id]);

if (type->Symbol->backendUserdata && !FlagDebug) {
BackendUserdataAndDebug *userdata = (BackendUserdataAndDebug *)type->Symbol->backendUserdata;
return userdata->type;
}

Type_Enum enm = type->Enum;

llvm::Type *ty;
if (type->Symbol->backendUserdata) {
ty = ((BackendUserdataAndDebug *)type->Symbol->backendUserdata)->type;
} else {
ty = canonicalize(ctx, enm.backingType);
}

BackendUserdataAndDebug *userdata;
if (type->Symbol->backendUserdata) {
userdata = (BackendUserdataAndDebug *) type->Symbol->backendUserdata;
ASSERT_MSG(!userdata->debugType, "debugType is only set here, which means this run twice");
} else {
userdata = (BackendUserdataAndDebug *) ArenaAlloc(&ctx->arena, sizeof(*userdata));
userdata->type = ty;
type->Symbol->backendUserdata = userdata;
}

if (FlagDebug) {
llvm::DIType *dbTy = debugCanonicalize(ctx, enm.backingType);
std::vector<llvm::Metadata *> enumerations;

For (enm.cases) {
EnumField cse = enm.cases[i];
llvm::DIEnumerator *e = ctx->d.builder->createEnumerator(cse.name, cse.val);
enumerations.push_back(e);
}

auto elements = ctx->d.builder->getOrCreateArray(enumerations);
dbTy = ctx->d.builder->createEnumerationType(
ctx->d.scope,
type->Symbol->name,
ctx->d.file,
expr->start.line,
type->Width,
type->Align,
elements,
dbTy
);

userdata->debugType = dbTy;
}

return ty;
}

llvm::StructType *emitExprTypeStruct(Context *ctx, Expr *expr) {
ASSERT(expr->kind == ExprKind_TypeStruct);

Expand All @@ -1025,22 +1091,25 @@ llvm::StructType *emitExprTypeStruct(Context *ctx, Expr *expr) {
Type *fieldType = type->Struct.members[index]->type;

llvm::Type *ty = canonicalize(ctx, fieldType);
llvm::DIType *dty = debugCanonicalize(ctx, fieldType);
llvm::DIType *dty = NULL;
if (FlagDebug)
dty = debugCanonicalize(ctx, fieldType);

for (size_t j = 0; j < ArrayLen(item.names); j++) {
if (FlagDebug) {
llvm::DIDerivedType *member = ctx->d.builder->createMemberType(
ctx->d.scope,
item.names[j],
ctx->d.file,
item.start.line,
fieldType->Width,
fieldType->Align,
type->Struct.members[index]->offset,
llvm::DINode::DIFlags::FlagZero,
dty
);
llvm::DIDerivedType *member = ctx->d.builder->createMemberType(
ctx->d.scope,
item.names[j],
ctx->d.file,
item.start.line,
fieldType->Width,
fieldType->Align,
type->Struct.members[index]->offset,
llvm::DINode::DIFlags::FlagZero,
dty
);

debugMembers.push_back(member);
debugMembers.push_back(member);
}
elementTypes.push_back(ty);

Expand All @@ -1050,24 +1119,10 @@ llvm::StructType *emitExprTypeStruct(Context *ctx, Expr *expr) {

llvm::StructType *ty;
if (type->Symbol->backendUserdata) {
ty = (llvm::StructType *) type->Symbol->backendUserdata;
ty = ((BackendStructUserdata *) type->Symbol->backendUserdata)->type;
} else {
ty = llvm::StructType::create(ctx->m->getContext(), elementTypes);
}
llvm::DICompositeType *debugType;
if (FlagDebug) {
debugType = ctx->d.builder->createStructType(
ctx->d.scope,
type->Symbol->name,
ctx->d.file,
type->Symbol->decl->start.line,
type->Width,
type->Align,
llvm::DINode::DIFlags::FlagZero,
NULL, // DerivedFrom
ctx->d.builder->getOrCreateArray(debugMembers)
);
}

if (type->Symbol) {
ty->setName(type->Symbol->name);
Expand All @@ -1077,12 +1132,23 @@ llvm::StructType *emitExprTypeStruct(Context *ctx, Expr *expr) {
userdata = (BackendStructUserdata *) type->Symbol->backendUserdata;
ASSERT_MSG(!userdata->debugType, "debugType is only set here, which means this run twice");
} else {
userdata = (BackendStructUserdata *) ArenaAlloc(&ctx->arena, sizeof(BackendStructUserdata));
userdata = (BackendStructUserdata *) ArenaAlloc(&ctx->arena, sizeof(*userdata));
userdata->type = ty;
type->Symbol->backendUserdata = userdata;
}

if (FlagDebug) {
llvm::DICompositeType *debugType = ctx->d.builder->createStructType(
ctx->d.scope,
type->Symbol->name,
ctx->d.file,
type->Symbol->decl->start.line,
type->Width,
type->Align,
llvm::DINode::DIFlags::FlagZero,
NULL, // DerivedFrom
ctx->d.builder->getOrCreateArray(debugMembers)
);
userdata->debugType = debugType;
}
}
Expand Down Expand Up @@ -1136,6 +1202,7 @@ void emitDeclConstant(Context *ctx, Decl *decl) {
}

case ExprKind_TypeEnum: {
emitExprTypeEnum(ctx, decl->Constant.values[0]);
break;
}

Expand Down