array reflection
This commit is contained in:
+125
-46
@@ -231,7 +231,6 @@ Checker :: struct {
|
||||
constants: []Constant,
|
||||
template_diagnostics: []source.Diagnostic_Id,
|
||||
constant_stack: [dynamic]Constant_Frame,
|
||||
ast_expr_stack: [dynamic]ast.Expr_Id,
|
||||
hir_expr_stack: [dynamic]hir.Expr_Id,
|
||||
infer_stack: [dynamic]Infer_Frame,
|
||||
build_stack: [dynamic]Build_Expr_Frame,
|
||||
@@ -2090,6 +2089,9 @@ explicit_comptime_argument_valid :: proc(
|
||||
_, ok := resolve_type_argument(checker, arg, pkg, file)
|
||||
return ok
|
||||
}
|
||||
if type_pattern_mentions_comptime(checker, function, comptime_param_count(function), param.type) {
|
||||
return true
|
||||
}
|
||||
if is_comptime_string_param(checker, param, function) {
|
||||
_, ok := comptime_string_argument(checker, arg, pkg, file)
|
||||
return ok
|
||||
@@ -2097,9 +2099,6 @@ explicit_comptime_argument_valid :: proc(
|
||||
if types.is_concrete_integer(type_from_syntax(checker, param.type, function.pkg, function.file)) {
|
||||
return eval_integer_constant_in_context(checker, arg, pkg, file).kind == .Value
|
||||
}
|
||||
if type_pattern_mentions_comptime(checker, function, comptime_param_count(function), param.type) {
|
||||
return true
|
||||
}
|
||||
declared := type_from_syntax(checker, param.type, function.pkg, function.file)
|
||||
_, ok := eval_static_comptime_value(checker, param.name, arg, declared, pkg, file)
|
||||
return ok
|
||||
@@ -2307,6 +2306,38 @@ call_argument_mapping :: proc(
|
||||
}
|
||||
}
|
||||
}
|
||||
if selected_index < 0 {
|
||||
// Prefer the mapping that treats an array literal as a dependent aggregate.
|
||||
// It can then report the contextual element error instead of an ambiguous
|
||||
// hidden-comptime-parameter mapping failure.
|
||||
dependent_array_candidate := -1
|
||||
ambiguous := false
|
||||
for candidate, index in candidates {
|
||||
matches := false
|
||||
for arg_id, source_index in args {
|
||||
param_index := call_param_index(candidate, source_index)
|
||||
if param_index < 0 || param_index >= len(function.params) ||
|
||||
!function.params[param_index].comptime_value ||
|
||||
arg_id == ast.INVALID_EXPR || int(arg_id) >= len(checker.ast_module.exprs) ||
|
||||
checker.ast_module.exprs[arg_id].kind != .Array {
|
||||
continue
|
||||
}
|
||||
matches = matches || type_pattern_mentions_comptime(
|
||||
checker, function^, comptime_param_count(function^), function.params[param_index].type,
|
||||
)
|
||||
}
|
||||
if matches {
|
||||
if dependent_array_candidate >= 0 {
|
||||
ambiguous = true
|
||||
} else {
|
||||
dependent_array_candidate = index
|
||||
}
|
||||
}
|
||||
}
|
||||
if dependent_array_candidate >= 0 && !ambiguous {
|
||||
selected_index = dependent_array_candidate
|
||||
}
|
||||
}
|
||||
if selected_index < 0 {
|
||||
if len(failures) > 0 {
|
||||
builder := strings.builder_make(checker.allocator)
|
||||
@@ -2638,7 +2669,10 @@ infer_call_comptime_values :: proc(
|
||||
continue
|
||||
}
|
||||
values[ordinal].name = param.name
|
||||
if is_comptime_type_param(checker, param) {
|
||||
dependent := type_pattern_mentions_comptime(checker, function, ordinal, param.type)
|
||||
if dependent {
|
||||
values[ordinal].kind = .Static
|
||||
} else if is_comptime_type_param(checker, param) {
|
||||
values[ordinal].kind = .Type
|
||||
} else if is_comptime_string_param(checker, param, function) {
|
||||
values[ordinal].kind = .String
|
||||
@@ -2653,6 +2687,65 @@ infer_call_comptime_values :: proc(
|
||||
ordinal += 1
|
||||
}
|
||||
matched := true
|
||||
all_bound := true
|
||||
for value_bound in bound {
|
||||
all_bound = all_bound && value_bound
|
||||
}
|
||||
if !all_bound && is_runtime_type(checker, expected) {
|
||||
if types.is_valid(function.error) && types.kind(expected, &checker.module.types) == .Fallible {
|
||||
matched = match_inferred_type_pattern(
|
||||
checker, function, prefix, function.result,
|
||||
types.fallible_success(expected, &checker.module.types), values, bound,
|
||||
source.Span{}, diagnose,
|
||||
) && matched
|
||||
matched = match_inferred_type_pattern(
|
||||
checker, function, prefix, function.error,
|
||||
types.fallible_error(expected, &checker.module.types), values, bound,
|
||||
source.Span{}, diagnose,
|
||||
) && matched
|
||||
} else if type_pattern_mentions_comptime(checker, function, prefix, function.result) {
|
||||
matched = match_inferred_type_pattern(
|
||||
checker, function, prefix, function.result, expected, values, bound,
|
||||
source.Span{}, diagnose,
|
||||
) && matched
|
||||
}
|
||||
}
|
||||
// A comptime aggregate is evaluated only after its dependent type is known.
|
||||
// Bind a direct `[N]T` parameter's N from an array literal's syntax first;
|
||||
// this supplies the contextual element type for strings and enum literals.
|
||||
for arg_id, source_index in args {
|
||||
param_index := call_param_index(mapping, source_index)
|
||||
if param_index < 0 || param_index >= len(function.params) ||
|
||||
!function.params[param_index].comptime_value ||
|
||||
arg_id == ast.INVALID_EXPR || int(arg_id) >= len(checker.ast_module.exprs) ||
|
||||
checker.ast_module.exprs[arg_id].kind != .Array {
|
||||
continue
|
||||
}
|
||||
pattern, pattern_ok := types.node(&checker.module.types, function.params[param_index].type)
|
||||
if !pattern_ok || pattern.kind != .Array || !pattern.unresolved_count ||
|
||||
pattern.count_expr == u32(ast.INVALID_EXPR) || int(pattern.count_expr) >= len(checker.ast_module.exprs) {
|
||||
continue
|
||||
}
|
||||
count_expr := checker.ast_module.exprs[ast.Expr_Id(pattern.count_expr)]
|
||||
if count_expr.kind != .Name || symbol.is_valid(count_expr.qualifier) {
|
||||
continue
|
||||
}
|
||||
binding_index, is_binding := comptime_binding_index(function, prefix, count_expr.name)
|
||||
if !is_binding || bound[binding_index] {
|
||||
continue
|
||||
}
|
||||
param, param_ok := comptime_param_for_name(function, count_expr.name)
|
||||
if !param_ok || is_comptime_type_param(checker, param) {
|
||||
continue
|
||||
}
|
||||
values[binding_index] = Comptime_Value{
|
||||
name=count_expr.name,
|
||||
type=type_from_syntax(checker, param.type, function.pkg, function.file),
|
||||
value=i128(len(checker.ast_module.exprs[arg_id].args)),
|
||||
kind=.Integer,
|
||||
}
|
||||
bound[binding_index] = true
|
||||
}
|
||||
for arg_id, source_index in args {
|
||||
param_index := call_param_index(mapping, source_index)
|
||||
if param_index < 0 || param_index >= len(function.params) || !function.params[param_index].comptime_value ||
|
||||
@@ -2669,6 +2762,7 @@ infer_call_comptime_values :: proc(
|
||||
matched = false
|
||||
continue
|
||||
}
|
||||
dependent := type_pattern_mentions_comptime(checker, function, prefix, param.type)
|
||||
if is_comptime_type_param(checker, param) {
|
||||
actual, ok := resolve_type_argument(checker, arg_id, pkg, file)
|
||||
if !ok {
|
||||
@@ -2688,7 +2782,7 @@ infer_call_comptime_values :: proc(
|
||||
}
|
||||
values[binding_index] = Comptime_Value{name=param.name, type=actual, kind=.Type}
|
||||
bound[binding_index] = true
|
||||
} else if is_comptime_string_param(checker, param, function) {
|
||||
} else if !dependent && is_comptime_string_param(checker, param, function) {
|
||||
text, text_ok := comptime_string_argument(checker, arg_id, pkg, file)
|
||||
if !text_ok {
|
||||
if failure != nil && len(failure^) == 0 {
|
||||
@@ -2712,7 +2806,7 @@ infer_call_comptime_values :: proc(
|
||||
kind=.String,
|
||||
}
|
||||
bound[binding_index] = true
|
||||
} else if types.is_concrete_integer(type_from_syntax(checker, param.type, function.pkg, function.file)) {
|
||||
} else if !dependent && types.is_concrete_integer(type_from_syntax(checker, param.type, function.pkg, function.file)) {
|
||||
declared := type_from_syntax(checker, param.type, function.pkg, function.file)
|
||||
constant := eval_integer_constant_in_context(checker, arg_id, pkg, file)
|
||||
if constant.kind != .Value {
|
||||
@@ -2777,29 +2871,6 @@ infer_call_comptime_values :: proc(
|
||||
bound[binding_index] = true
|
||||
}
|
||||
}
|
||||
all_bound := true
|
||||
for value_bound in bound {
|
||||
all_bound = all_bound && value_bound
|
||||
}
|
||||
if !all_bound && is_runtime_type(checker, expected) {
|
||||
if types.is_valid(function.error) && types.kind(expected, &checker.module.types) == .Fallible {
|
||||
matched = match_inferred_type_pattern(
|
||||
checker, function, prefix, function.result,
|
||||
types.fallible_success(expected, &checker.module.types), values, bound,
|
||||
source.Span{}, diagnose,
|
||||
) && matched
|
||||
matched = match_inferred_type_pattern(
|
||||
checker, function, prefix, function.error,
|
||||
types.fallible_error(expected, &checker.module.types), values, bound,
|
||||
source.Span{}, diagnose,
|
||||
) && matched
|
||||
} else if type_pattern_mentions_comptime(checker, function, prefix, function.result) {
|
||||
matched = match_inferred_type_pattern(
|
||||
checker, function, prefix, function.result, expected, values, bound,
|
||||
source.Span{}, diagnose,
|
||||
) && matched
|
||||
}
|
||||
}
|
||||
all_bound = true
|
||||
for value_bound in bound {
|
||||
all_bound = all_bound && value_bound
|
||||
@@ -3461,12 +3532,9 @@ contains_name :: proc(names: []symbol.Id, name: symbol.Id) -> bool {
|
||||
}
|
||||
|
||||
mark_expr_imports_used :: proc(checker: ^Checker, expr_id: ast.Expr_Id, file: ast.File_Id) {
|
||||
stack := checker.ast_expr_stack
|
||||
clear_dynamic_array(&stack)
|
||||
defer {
|
||||
clear_dynamic_array(&stack)
|
||||
checker.ast_expr_stack = stack
|
||||
}
|
||||
stack: [dynamic]ast.Expr_Id
|
||||
stack.allocator = checker.allocator
|
||||
defer delete(stack)
|
||||
append(&stack, expr_id)
|
||||
for len(stack) > 0 {
|
||||
id := pop(&stack)
|
||||
@@ -3658,14 +3726,14 @@ validate_declarations :: proc(checker: ^Checker) {
|
||||
locals.allocator = checker.allocator
|
||||
comptime_prefix := 0
|
||||
for param in function.params {
|
||||
dependent := param.comptime_value && type_pattern_mentions_comptime(
|
||||
checker, function, comptime_prefix, param.type,
|
||||
)
|
||||
param_type := types.INVALID
|
||||
if param.comptime_value || !has_comptime {
|
||||
if param.comptime_value && !dependent || !has_comptime {
|
||||
param_type = type_from_syntax(checker, param.type, function.pkg, function.file)
|
||||
}
|
||||
if param.comptime_value && !signature_poisoned {
|
||||
dependent := type_pattern_mentions_comptime(
|
||||
checker, function, comptime_prefix, param.type, type_params_only=true,
|
||||
)
|
||||
if function.c_abi {
|
||||
checker.template_diagnostics[function_id] = source.add(
|
||||
checker.diagnostics,
|
||||
@@ -4091,11 +4159,12 @@ validate_meta_schema :: proc(checker: ^Checker) {
|
||||
return types.find_named(&checker.module.types, u32(pkg), u32(symbol.intern(checker.symbols, name)))
|
||||
}
|
||||
field_info := find(checker, meta_package, "FieldInfo")
|
||||
array_info := find(checker, meta_package, "ArrayInfo")
|
||||
record_info := find(checker, meta_package, "RecordInfo")
|
||||
enum_info := find(checker, meta_package, "EnumInfo")
|
||||
type_info := find(checker, meta_package, "TypeInfo")
|
||||
layout := find(checker, meta_package, "Layout")
|
||||
valid := types.is_valid(field_info) && types.is_valid(record_info) && types.is_valid(enum_info) &&
|
||||
valid := types.is_valid(array_info) && types.is_valid(field_info) && types.is_valid(record_info) && types.is_valid(enum_info) &&
|
||||
types.is_valid(type_info) && types.is_valid(layout)
|
||||
layout_item, layout_ok := types.node(&checker.module.types, layout)
|
||||
layout_members := types.enum_members_for(&checker.module.types, layout)
|
||||
@@ -4104,12 +4173,22 @@ validate_meta_schema :: proc(checker: ^Checker) {
|
||||
valid = symbol_text(checker, symbol.Id(layout_members[0].name)) == "auto" &&
|
||||
symbol_text(checker, symbol.Id(layout_members[1].name)) == "c"
|
||||
}
|
||||
array_item, array_ok := types.node(&checker.module.types, array_info)
|
||||
field_item, field_ok := types.node(&checker.module.types, field_info)
|
||||
record_item, record_ok := types.node(&checker.module.types, record_info)
|
||||
enum_item, enum_ok := types.node(&checker.module.types, enum_info)
|
||||
valid = valid && field_ok && field_item.kind == .Struct && !field_item.tuple && !field_item.c_layout &&
|
||||
valid = valid && array_ok && array_item.kind == .Struct && !array_item.tuple && !array_item.c_layout &&
|
||||
field_ok && field_item.kind == .Struct && !field_item.tuple && !field_item.c_layout &&
|
||||
record_ok && record_item.kind == .Struct && !record_item.tuple && !record_item.c_layout &&
|
||||
enum_ok && enum_item.kind == .Struct && !enum_item.tuple && !enum_item.c_layout
|
||||
array_fields := types.fields_for(&checker.module.types, array_info)
|
||||
valid = valid && len(array_fields) == 2
|
||||
if valid {
|
||||
valid = symbol_text(checker, symbol.Id(array_fields[0].name)) == "child" &&
|
||||
is_type_metatype_syntax(checker, array_fields[0].type) &&
|
||||
symbol_text(checker, symbol.Id(array_fields[1].name)) == "len" &&
|
||||
types.equal(array_fields[1].type, types.USIZE)
|
||||
}
|
||||
field_fields := types.fields_for(&checker.module.types, field_info)
|
||||
valid = valid && len(field_fields) == 3
|
||||
if valid {
|
||||
@@ -4151,9 +4230,10 @@ validate_meta_schema :: proc(checker: ^Checker) {
|
||||
for tag, index in expected_tags {
|
||||
field := type_fields[index]
|
||||
if symbol_text(checker, symbol.Id(field.name)) != tag ||
|
||||
(tag == "array" && !types.equal(field.type, array_info)) ||
|
||||
(tag == "record" && !types.equal(field.type, record_info)) ||
|
||||
(tag == "enum" && !types.equal(field.type, enum_info)) ||
|
||||
(tag != "record" && tag != "enum" && !types.is_void(field.type)) {
|
||||
(tag != "array" && tag != "record" && tag != "enum" && !types.is_void(field.type)) {
|
||||
valid = false
|
||||
break
|
||||
}
|
||||
@@ -4242,7 +4322,7 @@ validate_type_nodes :: proc(checker: ^Checker) {
|
||||
}
|
||||
item_name := symbol_text(checker, symbol.Id(item.name))
|
||||
comptime_meta := meta_package &&
|
||||
(item_name == "FieldInfo" || item_name == "RecordInfo" || item_name == "EnumInfo" || item_name == "TypeInfo")
|
||||
(item_name == "ArrayInfo" || item_name == "FieldInfo" || item_name == "RecordInfo" || item_name == "EnumInfo" || item_name == "TypeInfo")
|
||||
if item.c_layout && !item.opaque && item.field_count == 0 {
|
||||
source.add(
|
||||
checker.diagnostics,
|
||||
@@ -14047,7 +14127,6 @@ check :: proc(
|
||||
append(&checker.module.strings, strings.clone(value, allocator))
|
||||
}
|
||||
checker.constant_stack.allocator = allocator
|
||||
checker.ast_expr_stack.allocator = allocator
|
||||
checker.hir_expr_stack.allocator = allocator
|
||||
checker.infer_stack.allocator = allocator
|
||||
checker.build_stack.allocator = allocator
|
||||
@@ -14120,7 +14199,6 @@ check :: proc(
|
||||
delete(checker.constants, allocator)
|
||||
delete(checker.template_diagnostics, allocator)
|
||||
delete(checker.constant_stack)
|
||||
delete(checker.ast_expr_stack)
|
||||
delete(checker.hir_expr_stack)
|
||||
delete(checker.infer_stack)
|
||||
delete(checker.build_stack)
|
||||
@@ -14154,6 +14232,7 @@ check :: proc(
|
||||
delete(checker.comptime_keys)
|
||||
delete(checker.comptime_static_values)
|
||||
delete(checker.expand_context)
|
||||
delete(checker.anon_globals)
|
||||
}
|
||||
|
||||
for function, index in ast_module.functions {
|
||||
|
||||
Reference in New Issue
Block a user