enums
This commit is contained in:
@@ -72,6 +72,7 @@ Expr_Kind :: enum u8 {
|
||||
Array,
|
||||
None,
|
||||
Name,
|
||||
Enum_Literal,
|
||||
Address,
|
||||
Deref,
|
||||
Index,
|
||||
|
||||
@@ -636,7 +636,7 @@ mark_expr_imports_used :: proc(checker: ^Checker, expr_id: ast.Expr_Id, file: as
|
||||
append(&stack, expr.left)
|
||||
case .Add, .Sub, .Mul, .Div, .Index, .Orelse, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or, .Range:
|
||||
append(&stack, expr.left, expr.right)
|
||||
case .Invalid, .Integer, .Float, .String, .Bool, .None, .Name:
|
||||
case .Invalid, .Integer, .Float, .String, .Bool, .None, .Name, .Enum_Literal:
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -879,6 +879,28 @@ validate_type_nodes :: proc(checker: ^Checker) {
|
||||
symbol_text(checker, symbol.Id(item.name)),
|
||||
)
|
||||
}
|
||||
if item.kind == .Enum {
|
||||
if !item.declared || !types.is_concrete_integer(item.child) {
|
||||
source.addf(
|
||||
checker.diagnostics,
|
||||
source.Span{},
|
||||
"enum type '%s' requires a concrete integer backing type",
|
||||
symbol_text(checker, symbol.Id(item.name)),
|
||||
)
|
||||
} else {
|
||||
for member in types.enum_members_for(&checker.module.types, id) {
|
||||
if !fits_integer_type(member.value, item.child, checker.target) {
|
||||
source.addf(
|
||||
checker.diagnostics,
|
||||
source.Span{},
|
||||
"enum value %d does not fit in %s",
|
||||
member.value,
|
||||
types.name(item.child),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if item.has_sentinel {
|
||||
value := i128(item.sentinel)
|
||||
if types.is_signed(item.child, checker.target) {
|
||||
@@ -1101,6 +1123,8 @@ infer_compound_expr :: proc(
|
||||
return types.array(store, element, u64(len(expr.args)), false)
|
||||
case .None:
|
||||
return types.INVALID
|
||||
case .Enum_Literal:
|
||||
return types.INVALID
|
||||
case .Address:
|
||||
child := infer_nested_expr(checker, expr.left, locals, pkg, file, demanded)
|
||||
return types.pointer(store, child, false, false)
|
||||
@@ -1126,6 +1150,10 @@ infer_compound_expr :: proc(
|
||||
preserve := item.has_sentinel && expr.args[1] == ast.INVALID_EXPR
|
||||
return types.slice(store, item.child, item.mutable, preserve, item.sentinel)
|
||||
case .Field:
|
||||
if enum_type, enum_ok := enum_type_from_field_expr(checker, expr, pkg, file); enum_ok {
|
||||
_, member_ok := find_enum_member(checker, enum_type, expr.name)
|
||||
return enum_type if member_ok else types.INVALID
|
||||
}
|
||||
value := infer_nested_expr(checker, expr.left, locals, pkg, file, demanded)
|
||||
field_name := symbol_text(checker, expr.name)
|
||||
item, has_item := types.container(value, store)
|
||||
@@ -1220,7 +1248,7 @@ infer_expr :: proc(
|
||||
last = types.F64
|
||||
_ = pop(&stack)
|
||||
case .String, .Array, .None, .Address, .Deref, .Index, .Slice,
|
||||
.Field, .Unwrap, .Orelse, .Struct_Literal, .Keyed,
|
||||
.Field, .Unwrap, .Orelse, .Struct_Literal, .Keyed, .Enum_Literal,
|
||||
.Bool, .Not, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or, .Range:
|
||||
last = infer_compound_expr(checker, expr, locals, pkg, file, demanded)
|
||||
_ = pop(&stack)
|
||||
@@ -1250,6 +1278,14 @@ infer_expr :: proc(
|
||||
}
|
||||
}
|
||||
}
|
||||
if !types.is_valid(last) {
|
||||
if enum_type, enum_ok := enum_type_from_name_expr(checker, expr, pkg, file); enum_ok {
|
||||
_, member_ok := find_enum_member(checker, enum_type, expr.name)
|
||||
if member_ok {
|
||||
last = enum_type
|
||||
}
|
||||
}
|
||||
}
|
||||
if !types.is_valid(last) {
|
||||
target_pkg, available := expr_package(checker, expr, pkg, file)
|
||||
if available {
|
||||
@@ -1867,7 +1903,7 @@ promote_c_vararg_expr :: proc(checker: ^Checker, expr_id: hir.Expr_Id, span: sou
|
||||
)
|
||||
return invalid_hir_expr(checker, span, id, types.C_INT)
|
||||
}
|
||||
promoted := types.c_vararg_promotion(actual, checker.target)
|
||||
promoted := types.c_vararg_promotion(actual, checker.target, &checker.module.types)
|
||||
if types.equal(actual, promoted) {
|
||||
return expr_id
|
||||
}
|
||||
@@ -2055,6 +2091,75 @@ find_struct_field :: proc(checker: ^Checker, struct_type: types.Type, name: symb
|
||||
return 0, {}, false
|
||||
}
|
||||
|
||||
find_enum_member :: proc(checker: ^Checker, enum_type: types.Type, name: symbol.Id) -> (types.Enum_Member, bool) {
|
||||
for member in types.enum_members_for(&checker.module.types, enum_type) {
|
||||
if member.name == u32(name) {
|
||||
return member, true
|
||||
}
|
||||
}
|
||||
return {}, false
|
||||
}
|
||||
|
||||
enum_type_from_name_expr :: proc(
|
||||
checker: ^Checker,
|
||||
expr: ast.Expr,
|
||||
pkg: ast.Package_Id,
|
||||
file: ast.File_Id,
|
||||
) -> (types.Type, bool) {
|
||||
if !symbol.is_valid(expr.qualifier) ||
|
||||
find_import(checker, file, expr.qualifier) != ast.INVALID_IMPORT {
|
||||
return types.INVALID, false
|
||||
}
|
||||
enum_type := types.find_named(&checker.module.types, u32(pkg), u32(expr.qualifier))
|
||||
return enum_type, types.is_enum(enum_type, &checker.module.types)
|
||||
}
|
||||
|
||||
enum_type_from_field_expr :: proc(
|
||||
checker: ^Checker,
|
||||
expr: ast.Expr,
|
||||
pkg: ast.Package_Id,
|
||||
file: ast.File_Id,
|
||||
mark_used := false,
|
||||
) -> (types.Type, bool) {
|
||||
if expr.left == ast.INVALID_EXPR || int(expr.left) >= len(checker.ast_module.exprs) {
|
||||
return types.INVALID, false
|
||||
}
|
||||
base := checker.ast_module.exprs[expr.left]
|
||||
if base.kind != .Name || !symbol.is_valid(base.qualifier) {
|
||||
return types.INVALID, false
|
||||
}
|
||||
target_pkg, available := expr_package(checker, base, pkg, file, mark_used)
|
||||
if !available {
|
||||
return types.INVALID, false
|
||||
}
|
||||
enum_type := types.find_named(&checker.module.types, u32(target_pkg), u32(base.name))
|
||||
return enum_type, types.is_enum(enum_type, &checker.module.types)
|
||||
}
|
||||
|
||||
enum_member_hir :: proc(
|
||||
checker: ^Checker,
|
||||
enum_type: types.Type,
|
||||
name: symbol.Id,
|
||||
span: source.Span,
|
||||
) -> hir.Expr_Id {
|
||||
member, ok := find_enum_member(checker, enum_type, name)
|
||||
if !ok {
|
||||
id := source.addf(checker.diagnostics, span, "unknown enum member '%s'", symbol_text(checker, name))
|
||||
return invalid_hir_expr(checker, span, id, enum_type)
|
||||
}
|
||||
value := i64(member.value) if member.value < 0 else transmute(i64)u64(member.value)
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
kind=.Integer,
|
||||
span=span,
|
||||
type=enum_type,
|
||||
integer=value,
|
||||
target=hir.INVALID_REF,
|
||||
left=hir.INVALID_EXPR,
|
||||
right=hir.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
|
||||
build_function_value :: proc(
|
||||
checker: ^Checker,
|
||||
template: ast.Function_Id,
|
||||
@@ -2213,6 +2318,17 @@ build_compound_expr :: proc(
|
||||
kind=.None, span=expr.span, type=expected, target=hir.INVALID_REF,
|
||||
left=hir.INVALID_EXPR, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
case .Enum_Literal:
|
||||
if !types.is_enum(expected, store) {
|
||||
id := source.addf(
|
||||
checker.diagnostics,
|
||||
expr.span,
|
||||
"'.%s' requires an enum context",
|
||||
symbol_text(checker, expr.name),
|
||||
)
|
||||
return invalid_hir_expr(checker, expr.span, id, expected)
|
||||
}
|
||||
return enum_member_hir(checker, expected, expr.name, expr.span)
|
||||
case .Address:
|
||||
value := build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
if !hir_is_location(checker, value) {
|
||||
@@ -2278,6 +2394,9 @@ build_compound_expr :: proc(
|
||||
target=hir.INVALID_REF, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
case .Field:
|
||||
if enum_type, enum_ok := enum_type_from_field_expr(checker, expr, pkg, file, true); enum_ok {
|
||||
return enum_member_hir(checker, enum_type, expr.name, expr.span)
|
||||
}
|
||||
base := build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
base_type := checker.module.exprs[base].type
|
||||
field_name := symbol_text(checker, expr.name)
|
||||
@@ -2417,7 +2536,15 @@ build_compound_expr :: proc(
|
||||
left_const := eval_constant(checker, expr.left)
|
||||
right_const := eval_constant(checker, expr.right)
|
||||
left, right: hir.Expr_Id
|
||||
if right_const.kind == .Value && left_const.kind != .Value {
|
||||
left_expr := checker.ast_module.exprs[expr.left]
|
||||
right_expr := checker.ast_module.exprs[expr.right]
|
||||
if right_expr.kind == .Enum_Literal && left_expr.kind != .Enum_Literal {
|
||||
left = build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
right = build_nested_expr(checker, expr.right, locals, global_reads, calls, checker.module.exprs[left].type, pkg, file)
|
||||
} else if left_expr.kind == .Enum_Literal && right_expr.kind != .Enum_Literal {
|
||||
right = build_nested_expr(checker, expr.right, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
left = build_nested_expr(checker, expr.left, locals, global_reads, calls, checker.module.exprs[right].type, pkg, file)
|
||||
} else if right_const.kind == .Value && left_const.kind != .Value {
|
||||
left = build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
hint := checker.module.exprs[left].type
|
||||
right = build_nested_expr(checker, expr.right, locals, global_reads, calls, hint, pkg, file)
|
||||
@@ -2435,7 +2562,13 @@ build_compound_expr :: proc(
|
||||
return invalid_hir_expr(checker, expr.span, expr.diagnostic, types.BOOL)
|
||||
}
|
||||
operand_type := types.INVALID
|
||||
if types.is_bool(left_type) && types.is_bool(right_type) {
|
||||
if types.is_enum(left_type, store) || types.is_enum(right_type, store) {
|
||||
if !types.equal(left_type, right_type) || (expr.kind != .Eq && expr.kind != .Ne) {
|
||||
id := source.add(checker.diagnostics, expr.span, "enum values only support '==' and '!=' with the same enum type")
|
||||
return invalid_hir_expr(checker, expr.span, id, types.BOOL)
|
||||
}
|
||||
operand_type = left_type
|
||||
} else if types.is_bool(left_type) && types.is_bool(right_type) {
|
||||
if expr.kind != .Eq && expr.kind != .Ne {
|
||||
id := source.add(checker.diagnostics, expr.span, "bool values only support '==' and '!='")
|
||||
return invalid_hir_expr(checker, expr.span, id, types.BOOL)
|
||||
@@ -2617,7 +2750,8 @@ build_expr :: proc(
|
||||
switch expr.kind {
|
||||
case .String, .Array, .None, .Address, .Deref, .Index, .Slice,
|
||||
.Field, .Unwrap, .Orelse, .Struct_Literal, .Keyed,
|
||||
.Bool, .Not, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or, .Range:
|
||||
.Bool, .Not, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or, .Range,
|
||||
.Enum_Literal:
|
||||
last = build_compound_expr(
|
||||
checker, expr, locals, global_reads, calls, frame.expected, pkg, file,
|
||||
)
|
||||
@@ -2674,6 +2808,11 @@ build_expr :: proc(
|
||||
})
|
||||
}
|
||||
}
|
||||
if last == hir.INVALID_EXPR {
|
||||
if enum_type, enum_ok := enum_type_from_name_expr(checker, expr, pkg, file); enum_ok {
|
||||
last = enum_member_hir(checker, enum_type, expr.name, expr.span)
|
||||
}
|
||||
}
|
||||
if last == hir.INVALID_EXPR {
|
||||
target_pkg, available := expr_package(checker, expr, pkg, file, true)
|
||||
if !available {
|
||||
|
||||
@@ -81,6 +81,9 @@ Api :: struct {
|
||||
get_type_spelling: proc "c"(CXType) -> CXString,
|
||||
get_typedef_underlying_type: proc "c"(CXCursor) -> CXType,
|
||||
get_type_declaration: proc "c"(CXType) -> CXCursor,
|
||||
get_enum_decl_integer_type: proc "c"(CXCursor) -> CXType,
|
||||
get_enum_constant_value: proc "c"(CXCursor) -> i64,
|
||||
get_enum_constant_unsigned: proc "c"(CXCursor) -> u64,
|
||||
get_canonical_type: proc "c"(CXType) -> CXType,
|
||||
get_pointee_type: proc "c"(CXType) -> CXType,
|
||||
get_array_element_type: proc "c"(CXType) -> CXType,
|
||||
@@ -119,6 +122,7 @@ CXCursor_VarDecl :: i32(9)
|
||||
CXCursor_TypedefDecl :: i32(20)
|
||||
CXCursor_MacroDefinition :: i32(501)
|
||||
CXCursor_FieldDecl :: i32(6)
|
||||
CXCursor_EnumConstantDecl :: i32(7)
|
||||
|
||||
CXLinkage_External :: i32(4)
|
||||
CXTLS_None :: i32(0)
|
||||
@@ -201,6 +205,9 @@ load_api_from :: proc(path: string) -> (Api, bool) {
|
||||
load_proc(&api, "clang_getTypeSpelling", &api.get_type_spelling) &&
|
||||
load_proc(&api, "clang_getTypedefDeclUnderlyingType", &api.get_typedef_underlying_type) &&
|
||||
load_proc(&api, "clang_getTypeDeclaration", &api.get_type_declaration) &&
|
||||
load_proc(&api, "clang_getEnumDeclIntegerType", &api.get_enum_decl_integer_type) &&
|
||||
load_proc(&api, "clang_getEnumConstantDeclValue", &api.get_enum_constant_value) &&
|
||||
load_proc(&api, "clang_getEnumConstantDeclUnsignedValue", &api.get_enum_constant_unsigned) &&
|
||||
load_proc(&api, "clang_getCanonicalType", &api.get_canonical_type) &&
|
||||
load_proc(&api, "clang_getPointeeType", &api.get_pointee_type) &&
|
||||
load_proc(&api, "clang_getArrayElementType", &api.get_array_element_type) &&
|
||||
@@ -468,6 +475,10 @@ translate_type :: proc(ctx: ^Context, value: CXType, preferred_record_name := ""
|
||||
record := add_record(ctx, declaration, preferred_record_name)
|
||||
populate_record(ctx, record, declaration)
|
||||
return add_type(ctx, Type{kind=.Record, child=INVALID_TYPE, record=record})
|
||||
case CXType_Enum:
|
||||
declaration := ctx.api.get_type_declaration(value)
|
||||
backing := ctx.api.get_enum_decl_integer_type(declaration)
|
||||
return translate_type(ctx, backing, preferred_record_name, depth+1)
|
||||
case CXType_FunctionProto:
|
||||
result := translate_type(ctx, ctx.api.get_result_type(value), "", depth+1)
|
||||
if result == INVALID_TYPE {
|
||||
@@ -508,7 +519,7 @@ translate_type :: proc(ctx: ^Context, value: CXType, preferred_record_name := ""
|
||||
return INVALID_TYPE
|
||||
}
|
||||
return translate_type(ctx, canonical, preferred_record_name, depth+1)
|
||||
case CXType_Enum, CXType_FunctionNoProto,
|
||||
case CXType_FunctionNoProto,
|
||||
CXType_IncompleteArray, CXType_VariableArray, CXType_DependentSizedArray:
|
||||
return INVALID_TYPE
|
||||
}
|
||||
@@ -551,6 +562,49 @@ add_alias :: proc(ctx: ^Context, name: string, value: Type_Id, reason := "") {
|
||||
})
|
||||
}
|
||||
|
||||
enum_backing_unsigned :: proc(value: CXType) -> bool {
|
||||
switch value.kind {
|
||||
case CXType_Char_U, CXType_UChar, CXType_UShort, CXType_UInt, CXType_ULong, CXType_ULongLong:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
Enum_Constant_Context :: struct {
|
||||
ctx: ^Context,
|
||||
backing: Type_Id,
|
||||
unsigned: bool,
|
||||
}
|
||||
|
||||
visit_enum_constant :: proc "c"(cursor, parent: CXCursor, client_data: rawptr) -> i32 {
|
||||
context = runtime.default_context()
|
||||
enum_ctx := (^Enum_Constant_Context)(client_data)
|
||||
ctx := enum_ctx.ctx
|
||||
if ctx.api.get_cursor_kind(cursor) != CXCursor_EnumConstantDecl {
|
||||
return CXChildVisit_Continue
|
||||
}
|
||||
name := clone_cx_string(ctx.api, ctx.api.get_cursor_spelling(cursor), ctx.allocator)
|
||||
defer delete(name, ctx.allocator)
|
||||
if len(name) == 0 {
|
||||
return CXChildVisit_Continue
|
||||
}
|
||||
value := Macro_Value{kind=.Integer, type=enum_ctx.backing}
|
||||
if enum_ctx.unsigned {
|
||||
value.integer = ctx.api.get_enum_constant_unsigned(cursor)
|
||||
} else {
|
||||
signed := ctx.api.get_enum_constant_value(cursor)
|
||||
value.negative = signed < 0
|
||||
value.integer = u64(-i128(signed)) if signed < 0 else u64(signed)
|
||||
}
|
||||
append(&ctx.result.macros, Macro_Constant{
|
||||
name=fmt.aprintf("%s", name, allocator=ctx.allocator),
|
||||
type=enum_ctx.backing,
|
||||
value=value,
|
||||
reason=fmt.aprintf("", allocator=ctx.allocator),
|
||||
})
|
||||
return CXChildVisit_Continue
|
||||
}
|
||||
|
||||
is_macro_identifier :: proc(value: string) -> bool {
|
||||
if len(value) == 0 {
|
||||
return false
|
||||
@@ -1367,7 +1421,19 @@ visit_cursor :: proc "c"(cursor, parent: CXCursor, client_data: rawptr) -> i32 {
|
||||
add_alias(ctx, name, value)
|
||||
}
|
||||
case CXCursor_EnumDecl:
|
||||
add_unsupported(ctx, name, "C enums are not supported")
|
||||
backing_c := ctx.api.get_enum_decl_integer_type(cursor)
|
||||
backing := translate_type(ctx, backing_c)
|
||||
if backing == INVALID_TYPE {
|
||||
add_unsupported(ctx, name, "C enum backing type is not supported")
|
||||
break
|
||||
}
|
||||
add_alias(ctx, name, backing)
|
||||
enum_ctx := Enum_Constant_Context{
|
||||
ctx=ctx,
|
||||
backing=backing,
|
||||
unsigned=enum_backing_unsigned(backing_c),
|
||||
}
|
||||
_ = ctx.api.visit_children(cursor, visit_enum_constant, &enum_ctx)
|
||||
case CXCursor_VarDecl:
|
||||
if len(name) == 0 {
|
||||
return CXChildVisit_Continue
|
||||
|
||||
@@ -18,6 +18,7 @@ keyword_kind :: proc(text: string) -> token.Kind {
|
||||
case "c_func": return .Keyword_C_Func
|
||||
case "struct": return .Keyword_Struct
|
||||
case "c_struct": return .Keyword_C_Struct
|
||||
case "enum": return .Keyword_Enum
|
||||
case "distinct": return .Keyword_Distinct
|
||||
case "import": return .Keyword_Import
|
||||
case "return": return .Keyword_Return
|
||||
|
||||
+21
-15
@@ -189,11 +189,16 @@ function_result_type :: proc(function: ir.Function, store: ^types.Store) -> stri
|
||||
return llvm_type(function.result, store)
|
||||
}
|
||||
|
||||
c_abi_extension :: proc(value: types.Type, selected: target.Target) -> string {
|
||||
if !types.is_concrete_integer(value) {
|
||||
c_abi_extension :: proc(value: types.Type, store: ^types.Store) -> string {
|
||||
resolved := types.runtime_representation(value, store)
|
||||
if !types.is_concrete_integer(resolved) {
|
||||
return ""
|
||||
}
|
||||
switch target.c_integer_extension(selected, types.bits(value, selected), types.is_signed(value, selected)) {
|
||||
switch target.c_integer_extension(
|
||||
store.selected,
|
||||
types.bits(resolved, store.selected),
|
||||
types.is_signed(resolved, store.selected),
|
||||
) {
|
||||
case .Sign: return "signext"
|
||||
case .Zero: return "zeroext"
|
||||
case .None: return ""
|
||||
@@ -203,7 +208,7 @@ c_abi_extension :: proc(value: types.Type, selected: target.Target) -> string {
|
||||
|
||||
emit_function_result :: proc(builder: ^strings.Builder, function: ir.Function, store: ^types.Store) {
|
||||
if function.calling_convention == .C {
|
||||
extension := c_abi_extension(function.result, store.selected)
|
||||
extension := c_abi_extension(function.result, store)
|
||||
if len(extension) > 0 {
|
||||
fmt.sbprintf(builder, "%s ", extension)
|
||||
}
|
||||
@@ -423,7 +428,7 @@ emit_call_args :: proc(
|
||||
(instructions[arg].type if valid_instruction(instructions, arg) else types.INVALID)
|
||||
fmt.sbprintf(builder, "%s ", llvm_type(arg_type, store))
|
||||
if c_abi && index < len(param_types) {
|
||||
extension := c_abi_extension(arg_type, store.selected)
|
||||
extension := c_abi_extension(arg_type, store)
|
||||
if len(extension) > 0 {
|
||||
fmt.sbprintf(builder, "%s ", extension)
|
||||
}
|
||||
@@ -1200,11 +1205,12 @@ emit_instruction_stream :: proc(
|
||||
}
|
||||
from_type := instructions[instruction.a].type
|
||||
if types.equal(from_type, instruction.type) ||
|
||||
!types.equal(types.c_vararg_promotion(from_type, emitter.module.target), instruction.type) {
|
||||
!types.equal(types.c_vararg_promotion(from_type, emitter.module.target, &emitter.module.types), instruction.type) {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid C variadic promotion operand")
|
||||
continue
|
||||
}
|
||||
if types.bits(from_type, emitter.module.target) == types.bits(instruction.type, emitter.module.target) {
|
||||
from_repr := types.runtime_representation(from_type, &emitter.module.types)
|
||||
if types.bits(from_repr, emitter.module.target) == types.bits(instruction.type, emitter.module.target) {
|
||||
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = select i1 true, %s ", instruction_index, type_name)
|
||||
write_operand(&emitter.builder, instructions, instruction.a, from_type, &emitter.module.types)
|
||||
@@ -1213,8 +1219,8 @@ emit_instruction_stream :: proc(
|
||||
strings.write_string(&emitter.builder, "\n")
|
||||
continue
|
||||
}
|
||||
operation := "fpext" if types.is_float(from_type, emitter.module.target) else
|
||||
("sext" if types.is_signed(from_type, emitter.module.target) else "zext")
|
||||
operation := "fpext" if types.is_float(from_repr, emitter.module.target) else
|
||||
("sext" if types.is_signed(from_repr, emitter.module.target) else "zext")
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = %s %s ", instruction_index, operation, llvm_type(from_type, &emitter.module.types))
|
||||
write_operand(&emitter.builder, instructions, instruction.a, from_type, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, " to %s\n", llvm_type(instruction.type, &emitter.module.types))
|
||||
@@ -1367,7 +1373,7 @@ emit_instruction_stream :: proc(
|
||||
(instructions[arg].type if valid_instruction(instructions, arg) else types.INVALID)
|
||||
if index >= len(param_fields) &&
|
||||
(!types.is_c_vararg_type(expected, &emitter.module.types) ||
|
||||
!types.equal(types.c_vararg_promotion(expected, emitter.module.target), expected)) {
|
||||
!types.equal(types.c_vararg_promotion(expected, emitter.module.target, &emitter.module.types), expected)) {
|
||||
valid_args = false
|
||||
break
|
||||
}
|
||||
@@ -1409,7 +1415,7 @@ emit_instruction_stream :: proc(
|
||||
strings.write_string(&emitter.builder, "fastcc ")
|
||||
}
|
||||
if function_item.c_abi {
|
||||
extension := c_abi_extension(function_item.child, emitter.module.target)
|
||||
extension := c_abi_extension(function_item.child, &emitter.module.types)
|
||||
if len(extension) > 0 {
|
||||
fmt.sbprintf(&emitter.builder, "%s ", extension)
|
||||
}
|
||||
@@ -1455,7 +1461,7 @@ emit_instruction_stream :: proc(
|
||||
} else {
|
||||
fmt.sbprintf(&emitter.builder, "%s ", llvm_type(arg_type, &emitter.module.types))
|
||||
if function_item.c_abi && fixed {
|
||||
extension := c_abi_extension(arg_type, emitter.module.target)
|
||||
extension := c_abi_extension(arg_type, &emitter.module.types)
|
||||
if len(extension) > 0 {
|
||||
fmt.sbprintf(&emitter.builder, "%s ", extension)
|
||||
}
|
||||
@@ -1490,7 +1496,7 @@ emit_instruction_stream :: proc(
|
||||
(instructions[arg].type if valid_instruction(instructions, arg) else types.INVALID)
|
||||
if index >= len(target.param_types) &&
|
||||
(!types.is_c_vararg_type(expected, &emitter.module.types) ||
|
||||
!types.equal(types.c_vararg_promotion(expected, emitter.module.target), expected)) {
|
||||
!types.equal(types.c_vararg_promotion(expected, emitter.module.target, &emitter.module.types), expected)) {
|
||||
valid_args = false
|
||||
break
|
||||
}
|
||||
@@ -1572,7 +1578,7 @@ emit_instruction_stream :: proc(
|
||||
} else {
|
||||
fmt.sbprintf(&emitter.builder, "%s ", llvm_type(arg_type, &emitter.module.types))
|
||||
if target.calling_convention == .C && fixed {
|
||||
extension := c_abi_extension(arg_type, emitter.module.target)
|
||||
extension := c_abi_extension(arg_type, &emitter.module.types)
|
||||
if len(extension) > 0 {
|
||||
fmt.sbprintf(&emitter.builder, "%s ", extension)
|
||||
}
|
||||
@@ -1898,7 +1904,7 @@ emit_functions :: proc(emitter: ^Emitter) {
|
||||
type_name := c_abi_param_type(param_type, &emitter.module.types) if function.calling_convention == .C else llvm_type(param_type, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, "%s", type_name)
|
||||
if function.calling_convention == .C && !types.is_record(param_type, &emitter.module.types) {
|
||||
extension := c_abi_extension(param_type, emitter.module.target)
|
||||
extension := c_abi_extension(param_type, &emitter.module.types)
|
||||
if len(extension) > 0 {
|
||||
fmt.sbprintf(&emitter.builder, " %s", extension)
|
||||
}
|
||||
|
||||
@@ -1325,6 +1325,11 @@ canonical_type :: proc(
|
||||
module.type_store.nodes[index].child = canonical_type(module, item.child, mapping, visiting)
|
||||
return value
|
||||
}
|
||||
if item.kind == .Enum {
|
||||
mapping[index] = value
|
||||
module.type_store.nodes[index].child = canonical_type(module, item.child, mapping, visiting)
|
||||
return value
|
||||
}
|
||||
if item.kind == .Struct || item.kind == .Union {
|
||||
mapping[index] = value
|
||||
fields := types.fields_for(&module.type_store, value)
|
||||
|
||||
@@ -588,6 +588,21 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
|
||||
})
|
||||
case .Left_Bracket:
|
||||
return parse_array_literal(parser, nesting)
|
||||
case .Dot:
|
||||
start := advance(parser)
|
||||
member := current(parser)
|
||||
if member.kind != .Identifier {
|
||||
return invalid_expr(parser, member.span, "expected an enum member after '.'")
|
||||
}
|
||||
advance(parser)
|
||||
return add_expr(parser, ast.Expr{
|
||||
kind=.Enum_Literal,
|
||||
span=span_from(start.span, member.span),
|
||||
name=member.symbol,
|
||||
left=ast.INVALID_EXPR,
|
||||
right=ast.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
case .Identifier:
|
||||
first := advance(parser)
|
||||
name := first
|
||||
@@ -1490,6 +1505,118 @@ parse_distinct :: proc(parser: ^Parser, name: token.Token) {
|
||||
_ = finish_statement(parser)
|
||||
}
|
||||
|
||||
parse_enum :: proc(parser: ^Parser, name: token.Token) {
|
||||
start := advance(parser)
|
||||
explicit_backing := false
|
||||
backing := types.INVALID
|
||||
if _, ok := allow(parser, .Left_Paren); ok {
|
||||
explicit_backing = true
|
||||
backing = parse_type(parser)
|
||||
if _, close_ok := allow(parser, .Right_Paren); !close_ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected ')' after enum backing type")
|
||||
}
|
||||
}
|
||||
skip_newlines(parser)
|
||||
if _, ok := allow(parser, .Left_Brace); !ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected '{' after enum declaration")
|
||||
_ = finish_statement(parser)
|
||||
return
|
||||
}
|
||||
members: [dynamic]types.Enum_Member
|
||||
members.allocator = parser.module.allocator
|
||||
defer delete(members)
|
||||
next_value: i128
|
||||
previous_value: i128
|
||||
has_previous := false
|
||||
skip_newlines(parser)
|
||||
for current(parser).kind != .Right_Brace && current(parser).kind != .Eof {
|
||||
if current(parser).kind != .Identifier {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected an enum member name")
|
||||
for current(parser).kind != .Newline &&
|
||||
current(parser).kind != .Right_Brace &&
|
||||
current(parser).kind != .Eof {
|
||||
advance(parser)
|
||||
}
|
||||
skip_newlines(parser)
|
||||
continue
|
||||
}
|
||||
member := advance(parser)
|
||||
duplicate := false
|
||||
for existing in members {
|
||||
if existing.name == u32(member.symbol) {
|
||||
duplicate = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if duplicate {
|
||||
source.addf(parser.diagnostics, member.span, "duplicate enum member '%s'", token_text(parser, member))
|
||||
}
|
||||
value := next_value
|
||||
if _, ok := allow(parser, .Equal); ok {
|
||||
if !explicit_backing {
|
||||
source.add(parser.diagnostics, member.span, "explicit enum values require a backing type")
|
||||
}
|
||||
negative := false
|
||||
if _, minus_ok := allow(parser, .Minus); minus_ok {
|
||||
negative = true
|
||||
}
|
||||
literal := current(parser)
|
||||
if literal.kind != .Integer {
|
||||
source.add(parser.diagnostics, literal.span, "expected a decimal integer literal for enum value")
|
||||
} else {
|
||||
advance(parser)
|
||||
magnitude, magnitude_ok := parse_integer_magnitude(token_text(parser, literal))
|
||||
if !magnitude_ok {
|
||||
source.add(parser.diagnostics, literal.span, "enum value magnitude does not fit in u64")
|
||||
} else {
|
||||
value = i128(magnitude)
|
||||
if negative {
|
||||
value = -value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if has_previous && value <= previous_value {
|
||||
source.add(parser.diagnostics, member.span, "enum values must be strictly increasing")
|
||||
}
|
||||
if !duplicate {
|
||||
append(&members, types.Enum_Member{name=u32(member.symbol), value=value})
|
||||
}
|
||||
previous_value = value
|
||||
has_previous = true
|
||||
next_value = value+1
|
||||
if _, ok := allow(parser, .Comma); ok {
|
||||
skip_newlines(parser)
|
||||
} else {
|
||||
_ = finish_statement(parser, true)
|
||||
}
|
||||
}
|
||||
if _, ok := allow(parser, .Right_Brace); !ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected '}' after enum members")
|
||||
}
|
||||
if len(members) == 0 {
|
||||
source.add(parser.diagnostics, start.span, "enum declarations require at least one member")
|
||||
}
|
||||
if !explicit_backing {
|
||||
max_value := u64(max(len(members)-1, 0))
|
||||
backing = types.U8
|
||||
if max_value > 0xff {
|
||||
backing = types.U16
|
||||
}
|
||||
if max_value > 0xffff {
|
||||
backing = types.U32
|
||||
}
|
||||
if max_value > 0xffff_ffff {
|
||||
backing = types.U64
|
||||
}
|
||||
}
|
||||
id := types.named(&parser.module.type_store, u32(parser.pkg), u32(name.symbol))
|
||||
if !types.define_enum(&parser.module.type_store, id, backing, members[:], explicit_backing) {
|
||||
source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name))
|
||||
}
|
||||
_ = finish_statement(parser)
|
||||
}
|
||||
|
||||
decode_import_path :: proc(parser: ^Parser, tok: token.Token) -> string {
|
||||
text := token_text(parser, tok)
|
||||
if len(text) < 2 {
|
||||
@@ -1601,6 +1728,10 @@ parse_top_level :: proc(parser: ^Parser) {
|
||||
parse_struct(parser, name, current(parser).kind == .Keyword_C_Struct)
|
||||
return
|
||||
}
|
||||
if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_Enum {
|
||||
parse_enum(parser, name)
|
||||
return
|
||||
}
|
||||
if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_Distinct {
|
||||
parse_distinct(parser, name)
|
||||
return
|
||||
|
||||
@@ -52,6 +52,7 @@ Kind :: enum u8 {
|
||||
Keyword_C_Func,
|
||||
Keyword_Struct,
|
||||
Keyword_C_Struct,
|
||||
Keyword_Enum,
|
||||
Keyword_Distinct,
|
||||
Keyword_Import,
|
||||
Keyword_Return,
|
||||
|
||||
+72
-13
@@ -65,6 +65,7 @@ Kind :: enum u8 {
|
||||
Named,
|
||||
Alias,
|
||||
Distinct,
|
||||
Enum,
|
||||
Struct,
|
||||
Union,
|
||||
}
|
||||
@@ -91,6 +92,7 @@ Node :: struct {
|
||||
c_layout: bool,
|
||||
opaque: bool,
|
||||
declared: bool,
|
||||
explicit_backing: bool,
|
||||
}
|
||||
|
||||
Field :: struct {
|
||||
@@ -99,17 +101,24 @@ Field :: struct {
|
||||
offset: u64,
|
||||
}
|
||||
|
||||
Enum_Member :: struct {
|
||||
name: u32,
|
||||
value: i128,
|
||||
}
|
||||
|
||||
Store :: struct {
|
||||
nodes: [dynamic]Node,
|
||||
fields: [dynamic]Field,
|
||||
selected: target.Target,
|
||||
allocator: mem.Allocator,
|
||||
nodes: [dynamic]Node,
|
||||
fields: [dynamic]Field,
|
||||
enum_members: [dynamic]Enum_Member,
|
||||
selected: target.Target,
|
||||
allocator: mem.Allocator,
|
||||
}
|
||||
|
||||
init_store :: proc(allocator := context.allocator) -> Store {
|
||||
store: Store
|
||||
store.nodes.allocator = allocator
|
||||
store.fields.allocator = allocator
|
||||
store.enum_members.allocator = allocator
|
||||
store.selected = target.DEFAULT
|
||||
store.allocator = allocator
|
||||
return store
|
||||
@@ -118,19 +127,21 @@ init_store :: proc(allocator := context.allocator) -> Store {
|
||||
destroy_store :: proc(store: ^Store) {
|
||||
delete(store.nodes)
|
||||
delete(store.fields)
|
||||
delete(store.enum_members)
|
||||
}
|
||||
|
||||
clone_store :: proc(source: ^Store, allocator := context.allocator) -> Store {
|
||||
store := init_store(allocator)
|
||||
append(&store.nodes, ..source.nodes[:])
|
||||
append(&store.fields, ..source.fields[:])
|
||||
append(&store.enum_members, ..source.enum_members[:])
|
||||
store.selected = source.selected
|
||||
return store
|
||||
}
|
||||
|
||||
intern :: proc(store: ^Store, candidate: Node) -> Type {
|
||||
if candidate.kind != .Struct && candidate.kind != .Union &&
|
||||
candidate.kind != .Named && candidate.kind != .Distinct {
|
||||
candidate.kind != .Named && candidate.kind != .Distinct && candidate.kind != .Enum {
|
||||
for existing, index in store.nodes {
|
||||
if existing == candidate {
|
||||
return DYNAMIC_START+Type(index)
|
||||
@@ -146,7 +157,7 @@ named :: proc(store: ^Store, pkg, name: u32, qualifier: u32 = 0, file: u32 = 0xf
|
||||
normalized_file := file if qualifier != 0 else u32(0)
|
||||
for existing, index in store.nodes {
|
||||
if (existing.kind == .Named || existing.kind == .Alias || existing.kind == .Distinct ||
|
||||
existing.kind == .Struct || existing.kind == .Union) &&
|
||||
existing.kind == .Enum || existing.kind == .Struct || existing.kind == .Union) &&
|
||||
existing.pkg == pkg && existing.name == name && existing.qualifier == qualifier &&
|
||||
existing.file == normalized_file {
|
||||
return DYNAMIC_START+Type(index)
|
||||
@@ -158,7 +169,7 @@ named :: proc(store: ^Store, pkg, name: u32, qualifier: u32 = 0, file: u32 = 0xf
|
||||
find_named :: proc(store: ^Store, pkg, name: u32, qualifier: u32 = 0) -> Type {
|
||||
for existing, index in store.nodes {
|
||||
if (existing.kind == .Named || existing.kind == .Alias || existing.kind == .Distinct ||
|
||||
existing.kind == .Struct || existing.kind == .Union) &&
|
||||
existing.kind == .Enum || existing.kind == .Struct || existing.kind == .Union) &&
|
||||
existing.pkg == pkg && existing.name == name && existing.qualifier == qualifier {
|
||||
return DYNAMIC_START+Type(index)
|
||||
}
|
||||
@@ -190,6 +201,22 @@ define_distinct :: proc(store: ^Store, id, child: Type) -> bool {
|
||||
return true
|
||||
}
|
||||
|
||||
define_enum :: proc(store: ^Store, id, backing: Type, members: []Enum_Member, explicit_backing: bool) -> bool {
|
||||
existing, ok := node(store, id)
|
||||
if !ok || existing.kind != .Named || existing.declared {
|
||||
return false
|
||||
}
|
||||
index := int(id-DYNAMIC_START)
|
||||
store.nodes[index].kind = .Enum
|
||||
store.nodes[index].child = backing
|
||||
store.nodes[index].field_start = u32(len(store.enum_members))
|
||||
store.nodes[index].field_count = u32(len(members))
|
||||
store.nodes[index].explicit_backing = explicit_backing
|
||||
store.nodes[index].declared = true
|
||||
append(&store.enum_members, ..members)
|
||||
return true
|
||||
}
|
||||
|
||||
define_record :: proc(
|
||||
store: ^Store,
|
||||
id: Type,
|
||||
@@ -247,6 +274,19 @@ params_for :: proc(store: ^Store, value: Type) -> []Field {
|
||||
return store.fields[start:end]
|
||||
}
|
||||
|
||||
enum_members_for :: proc(store: ^Store, value: Type) -> []Enum_Member {
|
||||
item, ok := node(store, value)
|
||||
if !ok || item.kind != .Enum {
|
||||
return nil
|
||||
}
|
||||
start := int(item.field_start)
|
||||
end := start+int(item.field_count)
|
||||
if start < 0 || end > len(store.enum_members) {
|
||||
return nil
|
||||
}
|
||||
return store.enum_members[start:end]
|
||||
}
|
||||
|
||||
kind :: proc(value: Type, store: ^Store = nil) -> Kind {
|
||||
switch value {
|
||||
case INVALID:
|
||||
@@ -409,7 +449,8 @@ is_concrete_scalar :: proc(value: Type) -> bool {
|
||||
is_concrete :: proc(value: Type, store: ^Store = nil) -> bool {
|
||||
value_kind := kind(value, store)
|
||||
if value_kind == .Scalar || value_kind == .Array || value_kind == .Pointer ||
|
||||
value_kind == .Slice || value_kind == .Range || value_kind == .Optional {
|
||||
value_kind == .Slice || value_kind == .Range || value_kind == .Optional ||
|
||||
value_kind == .Enum {
|
||||
return true
|
||||
}
|
||||
if value_kind == .Struct || value_kind == .Union {
|
||||
@@ -460,6 +501,10 @@ is_distinct :: proc(value: Type, store: ^Store) -> bool {
|
||||
return kind(value, store) == .Distinct
|
||||
}
|
||||
|
||||
is_enum :: proc(value: Type, store: ^Store) -> bool {
|
||||
return kind(value, store) == .Enum
|
||||
}
|
||||
|
||||
resolve_alias :: proc(value: Type, store: ^Store, depth := 0) -> Type {
|
||||
if depth > 64 {
|
||||
return INVALID
|
||||
@@ -486,6 +531,9 @@ is_c_record_field_type :: proc(value: Type, store: ^Store, depth := 0) -> bool {
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if item.kind == .Enum {
|
||||
return item.explicit_backing && is_concrete_integer(item.child)
|
||||
}
|
||||
if item.kind == .Array {
|
||||
return item.count > 0 && !item.has_sentinel && !item.inferred_count &&
|
||||
is_c_record_field_type(item.child, store, depth+1)
|
||||
@@ -524,7 +572,7 @@ is_runtime_value :: proc(value: Type, store: ^Store, depth := 0) -> bool {
|
||||
item, ok := node(store, value)
|
||||
return ok && item.declared && !item.opaque && (!item.c_layout || item.field_count > 0)
|
||||
}
|
||||
if value_kind == .Distinct {
|
||||
if value_kind == .Distinct || value_kind == .Enum {
|
||||
item, ok := node(store, value)
|
||||
return ok && item.declared && is_runtime_value(item.child, store, depth+1)
|
||||
}
|
||||
@@ -541,7 +589,7 @@ runtime_representation :: proc(value: Type, store: ^Store, depth := 0) -> Type {
|
||||
return INVALID
|
||||
}
|
||||
item, ok := node(store, value)
|
||||
if !ok || item.kind != .Distinct {
|
||||
if !ok || (item.kind != .Distinct && item.kind != .Enum) {
|
||||
return value
|
||||
}
|
||||
return runtime_representation(item.child, store, depth+1)
|
||||
@@ -585,6 +633,9 @@ is_c_signature_type :: proc(value: Type, store: ^Store, allow_void := false) ->
|
||||
if contains_distinct(value, store) {
|
||||
return false
|
||||
}
|
||||
if item, ok := node(store, value); ok && item.kind == .Enum {
|
||||
return item.explicit_backing && is_concrete_integer(item.child)
|
||||
}
|
||||
return is_concrete_scalar(value) || is_pointer(value, store) || is_optional_pointer(value, store) ||
|
||||
(is_c_struct(value, store) && is_runtime_value(value, store))
|
||||
}
|
||||
@@ -621,7 +672,12 @@ is_c_integer_promotion_candidate :: proc(value: Type) -> bool {
|
||||
return value >= C_CHAR && value <= C_USHORT
|
||||
}
|
||||
|
||||
c_vararg_promotion :: proc(value: Type, selected := target.DEFAULT) -> Type {
|
||||
c_vararg_promotion :: proc(value: Type, selected := target.DEFAULT, store: ^Store = nil) -> Type {
|
||||
if store != nil {
|
||||
if item, ok := node(store, value); ok && item.kind == .Enum {
|
||||
return c_vararg_promotion(item.child, selected, store)
|
||||
}
|
||||
}
|
||||
if !is_concrete_scalar(value) {
|
||||
return value
|
||||
}
|
||||
@@ -642,6 +698,9 @@ c_vararg_promotion :: proc(value: Type, selected := target.DEFAULT) -> Type {
|
||||
}
|
||||
|
||||
is_c_vararg_type :: proc(value: Type, store: ^Store) -> bool {
|
||||
if item, ok := node(store, value); ok && item.kind == .Enum {
|
||||
return item.explicit_backing && is_concrete_integer(item.child)
|
||||
}
|
||||
return is_concrete_scalar(value) || is_pointer(value, store) || is_optional_pointer(value, store)
|
||||
}
|
||||
|
||||
@@ -909,7 +968,7 @@ size :: proc(value: Type, store: ^Store, selected := target.DEFAULT) -> u64 {
|
||||
return ((child_size+1+child_align-1)/child_align)*child_align
|
||||
case .Function:
|
||||
return 0
|
||||
case .Distinct:
|
||||
case .Distinct, .Enum:
|
||||
return size(child_type(value, store), store, selected)
|
||||
case .Struct:
|
||||
item, _ := node(store, value)
|
||||
@@ -952,7 +1011,7 @@ alignment_of :: proc(value: Type, store: ^Store, selected := target.DEFAULT) ->
|
||||
return alignment_of(child_type(value, store), store, selected)
|
||||
case .Function:
|
||||
return 1
|
||||
case .Distinct:
|
||||
case .Distinct, .Enum:
|
||||
return alignment_of(child_type(value, store), store, selected)
|
||||
case .Struct:
|
||||
item, _ := node(store, value)
|
||||
|
||||
Reference in New Issue
Block a user