richer formatting
This commit is contained in:
+160
-49
@@ -232,6 +232,8 @@ Checker :: struct {
|
||||
current_comptime_values: []Comptime_Value,
|
||||
static_state: Ct_State,
|
||||
static_bindings: [dynamic]Static_Binding,
|
||||
comptime_keys: [dynamic]string,
|
||||
comptime_static_values: [dynamic]Ct_Value_Id,
|
||||
inline_context: [dynamic]Inline_Expansion,
|
||||
type_factories: [dynamic]Type_Factory_Entry,
|
||||
generated_types: [dynamic]Generated_Type_Entry,
|
||||
@@ -528,7 +530,14 @@ static_field_value :: proc(checker: ^Checker, base, name: symbol.Id) -> (Ct_Valu
|
||||
if !ok || binding.value == INVALID_CT_VALUE || int(binding.value) >= len(checker.static_state.values) {
|
||||
return {}, false
|
||||
}
|
||||
value := checker.static_state.values[binding.value]
|
||||
return persistent_field_value(checker, binding.value, name)
|
||||
}
|
||||
|
||||
persistent_field_value :: proc(checker: ^Checker, root: Ct_Value_Id, name: symbol.Id) -> (Ct_Value, bool) {
|
||||
if root == INVALID_CT_VALUE || int(root) >= len(checker.static_state.values) {
|
||||
return {}, false
|
||||
}
|
||||
value := checker.static_state.values[root]
|
||||
index, _, found := find_struct_field(checker, value.type, name)
|
||||
children := ct_child_slice(&checker.static_state, value)
|
||||
if !found || index < 0 || index >= len(children) || children[index] == INVALID_CT_VALUE ||
|
||||
@@ -1705,7 +1714,9 @@ 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
|
||||
}
|
||||
return false
|
||||
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
|
||||
}
|
||||
|
||||
search_call_mappings :: proc(search: ^Call_Mapping_Search, param_index, source_index: int) {
|
||||
@@ -1966,13 +1977,15 @@ bind_inferred_comptime :: proc(
|
||||
if matches {
|
||||
if existing.kind == .Type {
|
||||
matches = types.equal(types.resolve_alias(existing.type, &checker.module.types), types.resolve_alias(value.type, &checker.module.types))
|
||||
} else if existing.kind == .Static {
|
||||
matches = existing.fingerprint == value.fingerprint && existing.key == value.key && types.equal(existing.type, value.type)
|
||||
} else {
|
||||
matches = existing.value == value.value && types.equal(existing.type, value.type)
|
||||
}
|
||||
}
|
||||
if !matches && diagnose {
|
||||
left := type_label(checker, existing.type) if existing.kind == .Type else fmt.aprintf("%d", existing.value, allocator=checker.allocator)
|
||||
right := type_label(checker, value.type) if value.kind == .Type else fmt.aprintf("%d", value.value, allocator=checker.allocator)
|
||||
left := type_label(checker, existing.type) if existing.kind == .Type else fmt.aprintf("<comptime value>", allocator=checker.allocator) if existing.kind == .Static else fmt.aprintf("%d", existing.value, allocator=checker.allocator)
|
||||
right := type_label(checker, value.type) if value.kind == .Type else fmt.aprintf("<comptime value>", allocator=checker.allocator) if value.kind == .Static else fmt.aprintf("%d", value.value, allocator=checker.allocator)
|
||||
source.addf(checker.diagnostics, span, "conflicting inference for comptime parameter '%s': %s and %s", symbol_text(checker, name), left, right)
|
||||
if existing.kind != .Type {
|
||||
delete(left, checker.allocator)
|
||||
@@ -2221,9 +2234,12 @@ infer_call_comptime_values :: proc(
|
||||
} else if is_comptime_string_param(checker, param, function) {
|
||||
values[ordinal].kind = .String
|
||||
values[ordinal].type = type_from_syntax(checker, param.type, function.pkg, function.file)
|
||||
} else {
|
||||
} else if types.is_concrete_integer(type_from_syntax(checker, param.type, function.pkg, function.file)) {
|
||||
values[ordinal].kind = .Integer
|
||||
values[ordinal].type = type_from_syntax(checker, param.type, function.pkg, function.file)
|
||||
} else {
|
||||
values[ordinal].kind = .Static
|
||||
values[ordinal].type = type_from_syntax(checker, param.type, function.pkg, function.file)
|
||||
}
|
||||
ordinal += 1
|
||||
}
|
||||
@@ -2287,7 +2303,7 @@ infer_call_comptime_values :: proc(
|
||||
kind=.String,
|
||||
}
|
||||
bound[binding_index] = true
|
||||
} else {
|
||||
} else if 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 {
|
||||
@@ -2321,6 +2337,32 @@ infer_call_comptime_values :: proc(
|
||||
}
|
||||
values[binding_index] = Comptime_Value{name=param.name, type=declared, value=constant.value, kind=.Integer}
|
||||
bound[binding_index] = true
|
||||
} else {
|
||||
declared := type_from_syntax(checker, param.type, function.pkg, function.file)
|
||||
available: [dynamic]Comptime_Value
|
||||
available.allocator = checker.allocator
|
||||
append(&available, ..checker.current_comptime_values)
|
||||
for prior, prior_index in values[:binding_index] {
|
||||
if bound[prior_index] {
|
||||
append(&available, prior)
|
||||
}
|
||||
}
|
||||
value, value_ok := eval_static_comptime_value(
|
||||
checker, param.name, arg_id, declared, pkg, file, available[:], diagnose,
|
||||
)
|
||||
delete(available)
|
||||
if !value_ok {
|
||||
if failure != nil && len(failure^) == 0 {
|
||||
failure^ = fmt.aprintf(
|
||||
"argument %d for comptime parameter '%s' has no stable comptime identity",
|
||||
source_index+1, symbol_text(checker, param.name), allocator=checker.allocator,
|
||||
)
|
||||
}
|
||||
matched = false
|
||||
continue
|
||||
}
|
||||
values[binding_index] = value
|
||||
bound[binding_index] = true
|
||||
}
|
||||
}
|
||||
all_bound := true
|
||||
@@ -2768,48 +2810,56 @@ collect_comptime_values :: proc(
|
||||
continue
|
||||
}
|
||||
declared := type_from_syntax(checker, param.type, function.pkg, function.file)
|
||||
if !types.is_concrete_integer(declared) {
|
||||
if diagnose {
|
||||
source.addf(
|
||||
checker.diagnostics,
|
||||
param.span,
|
||||
"comptime parameter '%s' requires a concrete integer type",
|
||||
symbol_text(checker, param.name),
|
||||
)
|
||||
if types.is_concrete_integer(declared) {
|
||||
constant := Constant{kind = .Not_Constant}
|
||||
if index < len(args) {
|
||||
constant = eval_integer_constant_in_context(checker, args[index], pkg, file, values=extra_values)
|
||||
}
|
||||
if constant.kind != .Value {
|
||||
if diagnose {
|
||||
source.addf(
|
||||
checker.diagnostics,
|
||||
span,
|
||||
"argument for comptime parameter '%s' must be a compile-time integer expression",
|
||||
symbol_text(checker, param.name),
|
||||
)
|
||||
}
|
||||
ok = false
|
||||
continue
|
||||
}
|
||||
if !fits_integer_type(constant.value, declared, checker.target) {
|
||||
if diagnose {
|
||||
source.addf(
|
||||
checker.diagnostics,
|
||||
span,
|
||||
"integer constant %d does not fit in %s",
|
||||
constant.value,
|
||||
types.name(declared),
|
||||
)
|
||||
}
|
||||
ok = false
|
||||
continue
|
||||
}
|
||||
append(&values, Comptime_Value{name=param.name, type=declared, value=constant.value})
|
||||
continue
|
||||
}
|
||||
if index >= len(args) || args[index] == ast.INVALID_EXPR {
|
||||
ok = false
|
||||
continue
|
||||
}
|
||||
constant := Constant{kind = .Not_Constant}
|
||||
if index < len(args) {
|
||||
constant = eval_integer_constant_in_context(checker, args[index], pkg, file, values=extra_values)
|
||||
}
|
||||
if constant.kind != .Value {
|
||||
if diagnose {
|
||||
source.addf(
|
||||
checker.diagnostics,
|
||||
span,
|
||||
"argument for comptime parameter '%s' must be a compile-time integer expression",
|
||||
symbol_text(checker, param.name),
|
||||
)
|
||||
}
|
||||
available: [dynamic]Comptime_Value
|
||||
available.allocator = checker.allocator
|
||||
append(&available, ..extra_values)
|
||||
append(&available, ..values[:])
|
||||
value, value_ok := eval_static_comptime_value(
|
||||
checker, param.name, args[index], declared, pkg, file, available[:], diagnose,
|
||||
)
|
||||
delete(available)
|
||||
if !value_ok {
|
||||
ok = false
|
||||
continue
|
||||
}
|
||||
if !fits_integer_type(constant.value, declared, checker.target) {
|
||||
if diagnose {
|
||||
source.addf(
|
||||
checker.diagnostics,
|
||||
span,
|
||||
"integer constant %d does not fit in %s",
|
||||
constant.value,
|
||||
types.name(declared),
|
||||
)
|
||||
}
|
||||
ok = false
|
||||
continue
|
||||
}
|
||||
append(&values, Comptime_Value{name=param.name, type=declared, value=constant.value})
|
||||
append(&values, value)
|
||||
}
|
||||
if !ok {
|
||||
delete(values)
|
||||
@@ -3111,12 +3161,11 @@ validate_declarations :: proc(checker: ^Checker) {
|
||||
)
|
||||
}
|
||||
if !is_type_metatype_syntax(checker, param.type) &&
|
||||
!types.is_concrete_integer(param_type) &&
|
||||
!is_comptime_string_param(checker, param, function) {
|
||||
!is_runtime_type(checker, param_type) && param_type != types.RANGE {
|
||||
checker.template_diagnostics[function_id] = source.addf(
|
||||
checker.diagnostics,
|
||||
param.span,
|
||||
"comptime parameter '%s' requires type, a concrete integer type, or immutable []u8",
|
||||
"comptime parameter '%s' requires type or a concrete value type",
|
||||
symbol_text(checker, param.name),
|
||||
)
|
||||
}
|
||||
@@ -3503,9 +3552,10 @@ validate_meta_schema :: proc(checker: ^Checker) {
|
||||
}
|
||||
field_info := find(checker, meta_package, "FieldInfo")
|
||||
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) &&
|
||||
valid := 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)
|
||||
@@ -3516,8 +3566,10 @@ validate_meta_schema :: proc(checker: ^Checker) {
|
||||
}
|
||||
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 &&
|
||||
record_ok && record_item.kind == .Struct && !record_item.tuple && !record_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
|
||||
field_fields := types.fields_for(&checker.module.types, field_info)
|
||||
valid = valid && len(field_fields) == 3
|
||||
if valid {
|
||||
@@ -3528,6 +3580,13 @@ validate_meta_schema :: proc(checker: ^Checker) {
|
||||
symbol_text(checker, symbol.Id(field_fields[2].name)) == "index" &&
|
||||
types.equal(field_fields[2].type, types.USIZE)
|
||||
}
|
||||
enum_fields := types.fields_for(&checker.module.types, enum_info)
|
||||
valid = valid && len(enum_fields) == 1
|
||||
if valid {
|
||||
fields_item, fields_ok := types.node(&checker.module.types, enum_fields[0].type)
|
||||
valid = symbol_text(checker, symbol.Id(enum_fields[0].name)) == "fields" && fields_ok &&
|
||||
fields_item.kind == .Slice && !fields_item.mutable && types.equal(fields_item.child, field_info)
|
||||
}
|
||||
record_fields := types.fields_for(&checker.module.types, record_info)
|
||||
valid = valid && len(record_fields) == 4
|
||||
if valid {
|
||||
@@ -3553,7 +3612,8 @@ validate_meta_schema :: proc(checker: ^Checker) {
|
||||
field := type_fields[index]
|
||||
if symbol_text(checker, symbol.Id(field.name)) != tag ||
|
||||
(tag == "record" && !types.equal(field.type, record_info)) ||
|
||||
(tag != "record" && !types.is_void(field.type)) {
|
||||
(tag == "enum" && !types.equal(field.type, enum_info)) ||
|
||||
(tag != "record" && tag != "enum" && !types.is_void(field.type)) {
|
||||
valid = false
|
||||
break
|
||||
}
|
||||
@@ -3623,7 +3683,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 == "TypeInfo")
|
||||
(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,
|
||||
@@ -4263,6 +4323,12 @@ infer_expr :: proc(
|
||||
}
|
||||
if !types.is_valid(last) && symbol.is_valid(expr.qualifier) &&
|
||||
find_import(checker, file, expr.qualifier) == ast.INVALID_IMPORT {
|
||||
if value, ok := current_comptime_value(checker, expr.qualifier);
|
||||
ok && value.kind == .Static {
|
||||
if _, field, found := find_struct_field(checker, value.type, expr.name); found {
|
||||
last = field.type
|
||||
}
|
||||
}
|
||||
if value, ok := current_static_binding(checker, expr.qualifier); ok {
|
||||
if _, field, found := find_struct_field(checker, value.type, expr.name); found {
|
||||
last = field.type
|
||||
@@ -4298,7 +4364,7 @@ infer_expr :: proc(
|
||||
if !types.is_valid(last) {
|
||||
if !symbol.is_valid(expr.qualifier) {
|
||||
if value, ok := current_comptime_value(checker, expr.name); ok {
|
||||
if value.kind == .Integer || value.kind == .String {
|
||||
if value.kind == .Integer || value.kind == .String || value.kind == .Static {
|
||||
last = value.type
|
||||
}
|
||||
}
|
||||
@@ -4385,6 +4451,13 @@ infer_expr :: proc(
|
||||
continue
|
||||
}
|
||||
if field_expr, handled := field_intrinsic_expr(checker, expr, pkg, file); handled {
|
||||
if enum_type, ok := resolve_type_argument(checker, field_expr.left, pkg, file);
|
||||
ok && types.is_enum(enum_type, &checker.module.types) {
|
||||
_, member_ok := find_enum_member(checker, enum_type, field_expr.name)
|
||||
last = enum_type if member_ok else types.INVALID
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
base_type := infer_nested_expr(
|
||||
checker, field_expr.left, locals, pkg, file, demanded, local_types,
|
||||
)
|
||||
@@ -6360,6 +6433,15 @@ build_qualified_value_field :: proc(
|
||||
value, ok := build_field_from_value(checker, expr, base, local.type)
|
||||
return value, true, ok
|
||||
}
|
||||
if value, ok := current_comptime_value(checker, expr.qualifier);
|
||||
ok && value.kind == .Static && value.static_value != INVALID_CT_VALUE &&
|
||||
int(value.static_value) < len(checker.static_state.values) {
|
||||
field_value, field_ok := persistent_field_value(checker, value.static_value, expr.name)
|
||||
if !field_ok {
|
||||
return hir.INVALID_EXPR, true, false
|
||||
}
|
||||
return build_static_value(checker, field_value, expr.span, types.INVALID), true, true
|
||||
}
|
||||
if global := find_global(checker, expr.qualifier, pkg, file); global != ast.INVALID_GLOBAL {
|
||||
base := build_global_reference(checker, global, expr.span, global_reads)
|
||||
value, ok := build_field_from_value(checker, expr, base, checker.global_types[global])
|
||||
@@ -7482,6 +7564,15 @@ build_expr :: proc(
|
||||
})
|
||||
}
|
||||
}
|
||||
if last == hir.INVALID_EXPR && symbol.is_valid(expr.qualifier) {
|
||||
if value, ok := current_comptime_value(checker, expr.qualifier);
|
||||
ok && value.kind == .Static && value.static_value != INVALID_CT_VALUE &&
|
||||
int(value.static_value) < len(checker.static_state.values) {
|
||||
if field_value, found := persistent_field_value(checker, value.static_value, expr.name); found {
|
||||
last = build_static_value(checker, field_value, expr.span, frame.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
if last == hir.INVALID_EXPR && symbol.is_valid(expr.qualifier) {
|
||||
if value, ok := static_field_value(checker, expr.qualifier, expr.name); ok {
|
||||
last = build_static_value(checker, value, expr.span, frame.expected)
|
||||
@@ -7558,6 +7649,11 @@ build_expr :: proc(
|
||||
checker, constant_expr, locals, global_reads, calls,
|
||||
frame.expected, pkg, file,
|
||||
)
|
||||
} else if value.kind == .Static && value.static_value != INVALID_CT_VALUE &&
|
||||
int(value.static_value) < len(checker.static_state.values) {
|
||||
last = build_static_value(
|
||||
checker, checker.static_state.values[value.static_value], expr.span, frame.expected,
|
||||
)
|
||||
} else {
|
||||
id := source.addf(
|
||||
checker.diagnostics,
|
||||
@@ -7628,6 +7724,12 @@ build_expr :: proc(
|
||||
continue
|
||||
}
|
||||
if field_expr, handled := field_intrinsic_expr(checker, expr, pkg, file); handled {
|
||||
if enum_type, ok := resolve_type_argument(checker, field_expr.left, pkg, file);
|
||||
ok && types.is_enum(enum_type, &checker.module.types) {
|
||||
last = enum_member_hir(checker, enum_type, field_expr.name, field_expr.span)
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
base := build_nested_expr(
|
||||
checker, field_expr.left, locals, global_reads, calls,
|
||||
types.INVALID, pkg, file,
|
||||
@@ -8315,6 +8417,8 @@ make_link_name :: proc(checker: ^Checker, id: Spec_Id) -> string {
|
||||
strings.write_byte(&builder, hex[byte>>4])
|
||||
strings.write_byte(&builder, hex[byte&0xf])
|
||||
}
|
||||
} else if value.kind == .Static {
|
||||
fmt.sbprintf(&builder, "__ca%d_%016x", len(value.key), value.fingerprint)
|
||||
} else {
|
||||
strings.write_string(&builder, "__cv")
|
||||
if value.value < 0 {
|
||||
@@ -12070,6 +12174,8 @@ check :: proc(
|
||||
checker.type_factory_origins.allocator = allocator
|
||||
checker.call_resolutions.allocator = allocator
|
||||
checker.static_bindings.allocator = allocator
|
||||
checker.comptime_keys.allocator = allocator
|
||||
checker.comptime_static_values.allocator = allocator
|
||||
checker.inline_context.allocator = allocator
|
||||
checker.static_state = ct_state_make(&checker, 0, ast.INVALID_FILE)
|
||||
build_symbol_indexes(&checker)
|
||||
@@ -12146,6 +12252,11 @@ check :: proc(
|
||||
delete(checker.call_resolutions)
|
||||
ct_state_destroy(&checker.static_state)
|
||||
delete(checker.static_bindings)
|
||||
for key in checker.comptime_keys {
|
||||
delete(key, allocator)
|
||||
}
|
||||
delete(checker.comptime_keys)
|
||||
delete(checker.comptime_static_values)
|
||||
delete(checker.inline_context)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user