-
Notifications
You must be signed in to change notification settings - Fork 2
Enum Types #49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Enum Types #49
Changes from 3 commits
918c06f
a7e9434
e7694b2
61223c2
027b74c
eb12b23
5b9df02
c684b1e
125bdda
a8298c1
42943bf
0d0f2d7
7b66d72
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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) { | ||
|
|
@@ -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; | ||
| } | ||
|
|
@@ -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); | ||
| } | ||
|
|
||
|
|
@@ -474,6 +474,10 @@ Conversion conversion(Type *type, Type *target) { | |
| return result; | ||
| } | ||
|
|
||
| if (type->kind == TypeKind_Enum && IsInteger(target)) { | ||
BrettRToomey marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return ConversionKind_None; | ||
BrettRToomey marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| if (IsFloat(type) && IsInteger(target)) { | ||
| result |= ConversionKind_FtoI & ConversionFlag_Float; | ||
| if (IsSigned(type)) result |= ConversionFlag_Signed; | ||
|
|
@@ -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)); | ||
|
|
||
|
|
@@ -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)) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
| } | ||
|
|
||
|
|
@@ -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"); | ||
|
||
| continue; | ||
| } | ||
|
|
||
|
|
@@ -945,6 +942,10 @@ Type *checkExprTypeEnum(Expr *expr, CheckerContext *ctx, Package *pkg) { | |
| storeInfoBasicExpr(pkg, expr, type, ctx); | ||
| ctx->mode = ExprMode_Type; | ||
| return type; | ||
|
|
||
| unresolved: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix the prior leak of if (fields) ArrayFree(fields); |
||
| ctx->mode = ExprMode_Unresolved; | ||
| return NULL; | ||
| } | ||
|
|
||
| Type *checkExprTypeFunction(Expr *expr, CheckerContext *ctx, Package *pkg) { | ||
|
|
@@ -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; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
| } | ||
|
|
||
| return llvm::ConstantInt::get(type, info.value.Enum.value); | ||
| } | ||
|
|
||
| case SelectorKind_Import: { | ||
| Symbol *symbol = info.value.Import.symbol; | ||
| if (!symbol->backendUserdata) { | ||
|
|
@@ -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); | ||
|
|
||
|
|
@@ -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) | ||
vdka marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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); | ||
|
|
||
|
|
@@ -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); | ||
|
|
@@ -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; | ||
| } | ||
| } | ||
|
|
@@ -1136,6 +1202,7 @@ void emitDeclConstant(Context *ctx, Decl *decl) { | |
| } | ||
|
|
||
| case ExprKind_TypeEnum: { | ||
| emitExprTypeEnum(ctx, decl->Constant.values[0]); | ||
| break; | ||
BrettRToomey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.