enums
This commit is contained in:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user