comptime-state propagation through generated struct field resolution
This commit is contained in:
@@ -1099,6 +1099,7 @@ type_from_syntax :: proc(
|
|||||||
pkg := ast.Package_Id(0),
|
pkg := ast.Package_Id(0),
|
||||||
file := ast.File_Id(0),
|
file := ast.File_Id(0),
|
||||||
depth := 0,
|
depth := 0,
|
||||||
|
active_state: ^Ct_State = nil,
|
||||||
) -> types.Type {
|
) -> types.Type {
|
||||||
if depth > 64 {
|
if depth > 64 {
|
||||||
return types.INVALID
|
return types.INVALID
|
||||||
@@ -1116,9 +1117,9 @@ type_from_syntax :: proc(
|
|||||||
changed := false
|
changed := false
|
||||||
#partial switch item.kind {
|
#partial switch item.kind {
|
||||||
case .Alias:
|
case .Alias:
|
||||||
return type_from_syntax(checker, item.child, pkg, file, depth+1)
|
return type_from_syntax(checker, item.child, pkg, file, depth+1, active_state)
|
||||||
case .Array:
|
case .Array:
|
||||||
child := type_from_syntax(checker, item.child, pkg, file, depth+1)
|
child := type_from_syntax(checker, item.child, pkg, file, depth+1, active_state)
|
||||||
changed = changed || child != item.child
|
changed = changed || child != item.child
|
||||||
item.child = child
|
item.child = child
|
||||||
if item.unresolved_count {
|
if item.unresolved_count {
|
||||||
@@ -1127,7 +1128,8 @@ type_from_syntax :: proc(
|
|||||||
if expr_id != ast.INVALID_EXPR && int(expr_id) < len(checker.ast_module.exprs) {
|
if expr_id != ast.INVALID_EXPR && int(expr_id) < len(checker.ast_module.exprs) {
|
||||||
span = checker.ast_module.exprs[expr_id].span
|
span = checker.ast_module.exprs[expr_id].span
|
||||||
}
|
}
|
||||||
constant := eval_integer_constant_in_context(checker, expr_id, pkg, file)
|
constant := eval_integer_constant_in_state(active_state, expr_id) if active_state != nil else
|
||||||
|
eval_integer_constant_in_context(checker, expr_id, pkg, file)
|
||||||
if constant.kind == .Value {
|
if constant.kind == .Value {
|
||||||
switch {
|
switch {
|
||||||
case constant.value < 0:
|
case constant.value < 0:
|
||||||
@@ -1153,14 +1155,14 @@ type_from_syntax :: proc(
|
|||||||
}
|
}
|
||||||
return types.intern(store, item)
|
return types.intern(store, item)
|
||||||
case .Pointer, .Slice, .Optional, .Range, .Fallible:
|
case .Pointer, .Slice, .Optional, .Range, .Fallible:
|
||||||
child := type_from_syntax(checker, item.child, pkg, file, depth+1)
|
child := type_from_syntax(checker, item.child, pkg, file, depth+1, active_state)
|
||||||
extra := type_from_syntax(checker, item.extra, pkg, file, depth+1)
|
extra := type_from_syntax(checker, item.extra, pkg, file, depth+1, active_state)
|
||||||
item.child = child
|
item.child = child
|
||||||
item.extra = extra
|
item.extra = extra
|
||||||
return types.intern(store, item)
|
return types.intern(store, item)
|
||||||
case .Distinct, .Enum:
|
case .Distinct, .Enum:
|
||||||
child := type_from_syntax(checker, item.child, pkg, file, depth+1)
|
child := type_from_syntax(checker, item.child, pkg, file, depth+1, active_state)
|
||||||
extra := type_from_syntax(checker, item.extra, pkg, file, depth+1)
|
extra := type_from_syntax(checker, item.extra, pkg, file, depth+1, active_state)
|
||||||
changed = child != item.child || extra != item.extra
|
changed = child != item.child || extra != item.extra
|
||||||
item.child = child
|
item.child = child
|
||||||
item.extra = extra
|
item.extra = extra
|
||||||
@@ -1169,13 +1171,13 @@ type_from_syntax :: proc(
|
|||||||
resolved_params := make([]types.Type, len(params), checker.allocator)
|
resolved_params := make([]types.Type, len(params), checker.allocator)
|
||||||
defer delete(resolved_params, checker.allocator)
|
defer delete(resolved_params, checker.allocator)
|
||||||
for param, index in params {
|
for param, index in params {
|
||||||
resolved_params[index] = type_from_syntax(checker, param.type, pkg, file, depth+1)
|
resolved_params[index] = type_from_syntax(checker, param.type, pkg, file, depth+1, active_state)
|
||||||
}
|
}
|
||||||
result := type_from_syntax(checker, item.child, pkg, file, depth+1)
|
result := type_from_syntax(checker, item.child, pkg, file, depth+1, active_state)
|
||||||
return types.function(store, resolved_params, result, item.c_abi, item.variadic)
|
return types.function(store, resolved_params, result, item.c_abi, item.variadic)
|
||||||
case .Sum:
|
case .Sum:
|
||||||
left := type_from_syntax(checker, item.child, pkg, file, depth+1)
|
left := type_from_syntax(checker, item.child, pkg, file, depth+1, active_state)
|
||||||
right := type_from_syntax(checker, item.extra, pkg, file, depth+1)
|
right := type_from_syntax(checker, item.extra, pkg, file, depth+1, active_state)
|
||||||
composed, compose_error := types.compose_sum(store, left, right)
|
composed, compose_error := types.compose_sum(store, left, right)
|
||||||
if compose_error == .Unsupported {
|
if compose_error == .Unsupported {
|
||||||
source.add(checker.diagnostics, source.Span{}, "only native unbacked enums and tagged unions can be composed with '|'")
|
source.add(checker.diagnostics, source.Span{}, "only native unbacked enums and tagged unions can be composed with '|'")
|
||||||
@@ -2947,7 +2949,7 @@ resolved_call_arg_expected :: proc(
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve_generated_struct_type :: proc(checker: ^Checker, expr_id: ast.Expr_Id, pkg: ast.Package_Id, file: ast.File_Id) -> types.Type {
|
resolve_generated_struct_type :: proc(checker: ^Checker, expr_id: ast.Expr_Id, pkg: ast.Package_Id, file: ast.File_Id, active_state: ^Ct_State) -> types.Type {
|
||||||
for entry in checker.generated_types {
|
for entry in checker.generated_types {
|
||||||
if entry.expr == expr_id && comptime_values_equal(entry.values, checker.current_comptime_values) {
|
if entry.expr == expr_id && comptime_values_equal(entry.values, checker.current_comptime_values) {
|
||||||
return entry.result
|
return entry.result
|
||||||
@@ -2966,7 +2968,7 @@ resolve_generated_struct_type :: proc(checker: ^Checker, expr_id: ast.Expr_Id, p
|
|||||||
fields := make([]types.Field, len(template_fields), checker.allocator)
|
fields := make([]types.Field, len(template_fields), checker.allocator)
|
||||||
defer delete(fields, checker.allocator)
|
defer delete(fields, checker.allocator)
|
||||||
for field, index in template_fields {
|
for field, index in template_fields {
|
||||||
resolved := type_from_syntax(checker, field.type, pkg, file)
|
resolved := type_from_syntax(checker, field.type, pkg, file, active_state=active_state)
|
||||||
if (!is_runtime_type(checker, resolved) && !is_comptime_value_type(checker, resolved)) || types.is_void(resolved) {
|
if (!is_runtime_type(checker, resolved) && !is_comptime_value_type(checker, resolved)) || types.is_void(resolved) {
|
||||||
if expr.tuple {
|
if expr.tuple {
|
||||||
source.addf(checker.diagnostics, expr.span, "tuple element %d requires a concrete runtime type, got %s", index, type_label(checker, resolved))
|
source.addf(checker.diagnostics, expr.span, "tuple element %d requires a concrete runtime type, got %s", index, type_label(checker, resolved))
|
||||||
|
|||||||
@@ -1150,7 +1150,7 @@ ct_eval_expr :: proc(
|
|||||||
resolved := type_from_syntax(checker, expr.type, state.pkg, state.file)
|
resolved := type_from_syntax(checker, expr.type, state.pkg, state.file)
|
||||||
return ct_add_value(state, Ct_Value{kind=.Type, type=types.INVALID, index=u64(resolved)}), ct_flow(.Normal), types.is_valid(resolved)
|
return ct_add_value(state, Ct_Value{kind=.Type, type=types.INVALID, index=u64(resolved)}), ct_flow(.Normal), types.is_valid(resolved)
|
||||||
case .Anonymous_Struct_Type:
|
case .Anonymous_Struct_Type:
|
||||||
resolved := resolve_generated_struct_type(checker, expr_id, state.pkg, state.file)
|
resolved := resolve_generated_struct_type(checker, expr_id, state.pkg, state.file, state)
|
||||||
return ct_add_value(state, Ct_Value{kind=.Type, type=types.INVALID, index=u64(resolved)}), ct_flow(.Normal), types.is_valid(resolved)
|
return ct_add_value(state, Ct_Value{kind=.Type, type=types.INVALID, index=u64(resolved)}), ct_flow(.Normal), types.is_valid(resolved)
|
||||||
case .Enum_Literal:
|
case .Enum_Literal:
|
||||||
return ct_eval_enum_literal(state, expr, expected, depth+1)
|
return ct_eval_enum_literal(state, expr, expected, depth+1)
|
||||||
@@ -4451,7 +4451,17 @@ eval_integer_constant_in_context :: proc(
|
|||||||
}
|
}
|
||||||
state := ct_state_make(checker, pkg, file, types.INVALID, values, diagnose=false)
|
state := ct_state_make(checker, pkg, file, types.INVALID, values, diagnose=false)
|
||||||
defer ct_state_destroy(&state)
|
defer ct_state_destroy(&state)
|
||||||
value, flow, ok := ct_eval_expr(&state, expr_id, types.INVALID, depth)
|
return eval_integer_constant_in_state(&state, expr_id, depth)
|
||||||
|
}
|
||||||
|
|
||||||
|
eval_integer_constant_in_state :: proc(state: ^Ct_State, expr_id: ast.Expr_Id, depth := 0) -> Constant {
|
||||||
|
if depth > 64 || expr_id == ast.INVALID_EXPR || int(expr_id) >= len(state.checker.ast_module.exprs) {
|
||||||
|
return Constant{kind=.Not_Constant}
|
||||||
|
}
|
||||||
|
previous_silent := state.silent
|
||||||
|
state.silent = true
|
||||||
|
defer state.silent = previous_silent
|
||||||
|
value, flow, ok := ct_eval_expr(state, expr_id, types.INVALID, depth)
|
||||||
if !ok || flow.kind != .Normal {
|
if !ok || flow.kind != .Normal {
|
||||||
#partial switch state.error {
|
#partial switch state.error {
|
||||||
case .Overflow:
|
case .Overflow:
|
||||||
@@ -4465,7 +4475,7 @@ eval_integer_constant_in_context :: proc(
|
|||||||
}
|
}
|
||||||
return Constant{kind=.Not_Constant}
|
return Constant{kind=.Not_Constant}
|
||||||
}
|
}
|
||||||
integer, integer_ok := ct_integer_value(&state, value)
|
integer, integer_ok := ct_integer_value(state, value)
|
||||||
if !integer_ok {
|
if !integer_ok {
|
||||||
return Constant{kind=.Not_Constant}
|
return Constant{kind=.Not_Constant}
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-8
@@ -13819,17 +13819,29 @@ Generated func($T type) type {
|
|||||||
GeneratedInt :: alias Generated(i32)
|
GeneratedInt :: alias Generated(i32)
|
||||||
ordered Names = {}
|
ordered Names = {}
|
||||||
|
|
||||||
Map func($E, $V type) type { return struct { count usize } }
|
Map func($E, $V type) type {
|
||||||
|
|
||||||
init func($E, $V type, values meta.EnumFieldStruct(E, ?V, some!(none))) Map(E, V) {
|
|
||||||
count usize = 0
|
|
||||||
match typeinfo!(E) {
|
match typeinfo!(E) {
|
||||||
.enum |info|: expand for info.fields |field| {
|
.enum |info|: return struct {
|
||||||
if field!(values, field.name) |_| { count += 1 }
|
present [info.fields.len]mut bool
|
||||||
|
values [info.fields.len]mut V
|
||||||
}
|
}
|
||||||
else: compile_error!("EnumMap key must be an enum")
|
else: compile_error!("EnumMap key must be an enum")
|
||||||
}
|
}
|
||||||
return Map(E, V) {count = count}
|
}
|
||||||
|
|
||||||
|
init func($E, $V type, values meta.EnumFieldStruct(E, ?V, some!(none))) Map(E, V) {
|
||||||
|
map Map(E, V) = undefined
|
||||||
|
match typeinfo!(E) {
|
||||||
|
.enum |info|: expand for info.fields |field, index| {
|
||||||
|
map.present[index] = false
|
||||||
|
if field!(values, field.name) |value| {
|
||||||
|
map.present[index] = true
|
||||||
|
map.values[index] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else: compile_error!("EnumMap key must be an enum")
|
||||||
|
}
|
||||||
|
return map
|
||||||
}
|
}
|
||||||
|
|
||||||
main func() i32 {
|
main func() i32 {
|
||||||
@@ -13875,7 +13887,8 @@ main func() i32 {
|
|||||||
ident = "identifier",
|
ident = "identifier",
|
||||||
int = "integer",
|
int = "integer",
|
||||||
})
|
})
|
||||||
if map.count != 2 { return 9 }
|
if !map.present[0] or !map.present[1] or map.present[2] { return 9 }
|
||||||
|
if map.values[0].len != 10 or map.values[1].len != 7 { return 18 }
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|||||||
Reference in New Issue
Block a user