diagnostics upgrade
This commit is contained in:
+277
-35
@@ -208,6 +208,7 @@ Checker :: struct {
|
||||
record_field_conflicts: []types.Type,
|
||||
record_field_conflict_spans: []source.Span,
|
||||
record_field_demands_dirty: bool,
|
||||
poisoned_packages: []bool,
|
||||
external_global_canonical: []ast.Global_Id,
|
||||
external_global_diagnostics: []source.Diagnostic_Id,
|
||||
constants: []Constant,
|
||||
@@ -1496,16 +1497,30 @@ expr_lookup_file :: proc(expr: ast.Expr, file: ast.File_Id) -> ast.File_Id {
|
||||
return ast.INVALID_FILE if symbol.is_valid(expr.qualifier) else file
|
||||
}
|
||||
|
||||
expr_symbol_span :: proc(checker: ^Checker, expr: ast.Expr, name: symbol.Id) -> source.Span {
|
||||
span := expr.span
|
||||
span.end = span.start+source.Offset(len(symbol_text(checker, name)))
|
||||
return span
|
||||
}
|
||||
|
||||
add_package_resolution_diagnostic :: proc(checker: ^Checker, expr: ast.Expr, file: ast.File_Id) -> source.Diagnostic_Id {
|
||||
if find_import(checker, file, expr.qualifier) == ast.INVALID_IMPORT {
|
||||
return source.addf(checker.diagnostics, expr.span, "unknown package alias '%s'", symbol_text(checker, expr.qualifier))
|
||||
span := expr_symbol_span(checker, expr, expr.qualifier)
|
||||
id := source.addf(checker.diagnostics, span, "unknown symbol '%s'", symbol_text(checker, expr.qualifier))
|
||||
source.set_primary_label(checker.diagnostics, id, "unknown symbol")
|
||||
return id
|
||||
}
|
||||
return source.addf(checker.diagnostics, expr.span, "unavailable imported package '%s'", symbol_text(checker, expr.qualifier))
|
||||
}
|
||||
|
||||
add_name_resolution_diagnostic :: proc(checker: ^Checker, expr: ast.Expr, target_pkg: ast.Package_Id, file: ast.File_Id) -> source.Diagnostic_Id {
|
||||
if find_template(checker, expr.name, target_pkg, expr_lookup_file(expr, file)) != ast.INVALID_FUNCTION {
|
||||
return source.addf(checker.diagnostics, expr.span, "'%s' is a function, not a global value", symbol_text(checker, expr.name))
|
||||
template := find_template(checker, expr.name, target_pkg, expr_lookup_file(expr, file))
|
||||
id := source.addf(checker.diagnostics, expr_symbol_span(checker, expr, expr.name), "'%s' is a function, not a global value", symbol_text(checker, expr.name))
|
||||
if !checker.ast_module.functions[template].imported {
|
||||
source.add_secondary_label(checker.diagnostics, id, checker.ast_module.functions[template].span, "function declared here")
|
||||
}
|
||||
return id
|
||||
}
|
||||
if symbol.is_valid(expr.qualifier) {
|
||||
return source.addf(
|
||||
@@ -1516,12 +1531,20 @@ add_name_resolution_diagnostic :: proc(checker: ^Checker, expr: ast.Expr, target
|
||||
symbol_text(checker, expr.name),
|
||||
)
|
||||
}
|
||||
return source.addf(checker.diagnostics, expr.span, "unresolved global '%s'", symbol_text(checker, expr.name))
|
||||
span := expr_symbol_span(checker, expr, expr.name)
|
||||
id := source.addf(checker.diagnostics, span, "unknown symbol '%s'", symbol_text(checker, expr.name))
|
||||
source.set_primary_label(checker.diagnostics, id, "unknown symbol")
|
||||
return id
|
||||
}
|
||||
|
||||
add_call_resolution_diagnostic :: proc(checker: ^Checker, expr: ast.Expr, target_pkg: ast.Package_Id, file: ast.File_Id) -> source.Diagnostic_Id {
|
||||
if find_global(checker, expr.name, target_pkg, expr_lookup_file(expr, file)) != ast.INVALID_GLOBAL {
|
||||
return source.addf(checker.diagnostics, expr.span, "'%s' is a global, not a function", symbol_text(checker, expr.name))
|
||||
global := find_global(checker, expr.name, target_pkg, expr_lookup_file(expr, file))
|
||||
id := source.addf(checker.diagnostics, expr_symbol_span(checker, expr, expr.name), "'%s' is a global, not a function", symbol_text(checker, expr.name))
|
||||
if !checker.ast_module.globals[global].external {
|
||||
source.add_secondary_label(checker.diagnostics, id, checker.ast_module.globals[global].span, "global declared here")
|
||||
}
|
||||
return id
|
||||
}
|
||||
if symbol.is_valid(expr.qualifier) {
|
||||
return source.addf(
|
||||
@@ -1532,7 +1555,10 @@ add_call_resolution_diagnostic :: proc(checker: ^Checker, expr: ast.Expr, target
|
||||
symbol_text(checker, expr.name),
|
||||
)
|
||||
}
|
||||
return source.addf(checker.diagnostics, expr.span, "unresolved function '%s'", symbol_text(checker, expr.name))
|
||||
span := expr_symbol_span(checker, expr, expr.name)
|
||||
id := source.addf(checker.diagnostics, span, "unknown symbol '%s'", symbol_text(checker, expr.name))
|
||||
source.set_primary_label(checker.diagnostics, id, "unknown symbol")
|
||||
return id
|
||||
}
|
||||
|
||||
find_unsupported :: proc(checker: ^Checker, pkg: ast.Package_Id, name: symbol.Id) -> (ast.Unsupported, bool) {
|
||||
@@ -2649,6 +2675,7 @@ resolve_type_factory_call :: proc(checker: ^Checker, expr_id: ast.Expr_Id, pkg:
|
||||
}
|
||||
target_pkg, available := expr_package(checker, expr, pkg, file, true)
|
||||
if !available {
|
||||
_ = add_package_resolution_diagnostic(checker, expr, file)
|
||||
return types.INVALID
|
||||
}
|
||||
template := find_template(checker, expr.name, target_pkg, expr_lookup_file(expr, file))
|
||||
@@ -3145,6 +3172,7 @@ validate_declarations :: proc(checker: ^Checker) {
|
||||
continue
|
||||
}
|
||||
has_comptime := function_has_comptime_params(function)
|
||||
signature_poisoned := function.diagnostic != source.INVALID_DIAGNOSTIC
|
||||
locals: [dynamic]symbol.Id
|
||||
locals.allocator = checker.allocator
|
||||
for param in function.params {
|
||||
@@ -3152,7 +3180,7 @@ validate_declarations :: proc(checker: ^Checker) {
|
||||
if param.comptime_value || !has_comptime {
|
||||
param_type = type_from_syntax(checker, param.type, function.pkg, function.file)
|
||||
}
|
||||
if param.comptime_value {
|
||||
if param.comptime_value && !signature_poisoned {
|
||||
if function.c_abi {
|
||||
checker.template_diagnostics[function_id] = source.add(
|
||||
checker.diagnostics,
|
||||
@@ -3169,7 +3197,7 @@ validate_declarations :: proc(checker: ^Checker) {
|
||||
symbol_text(checker, param.name),
|
||||
)
|
||||
}
|
||||
} else if !has_comptime {
|
||||
} else if !has_comptime && !signature_poisoned {
|
||||
if diagnostic := add_unsupported_type_diagnostic(checker, param.span, param_type);
|
||||
diagnostic != source.INVALID_DIAGNOSTIC {
|
||||
checker.template_diagnostics[function_id] = diagnostic
|
||||
@@ -3197,7 +3225,7 @@ validate_declarations :: proc(checker: ^Checker) {
|
||||
)
|
||||
}
|
||||
append(&locals, param.name)
|
||||
if !has_comptime && types.contains_c_struct_by_value(param_type, &checker.module.types) {
|
||||
if !has_comptime && !signature_poisoned && types.contains_c_struct_by_value(param_type, &checker.module.types) {
|
||||
checker.template_diagnostics[function_id] = source.addf(
|
||||
checker.diagnostics,
|
||||
param.span,
|
||||
@@ -3206,7 +3234,7 @@ validate_declarations :: proc(checker: ^Checker) {
|
||||
)
|
||||
}
|
||||
}
|
||||
if !has_comptime {
|
||||
if !has_comptime && !signature_poisoned {
|
||||
result_type := type_from_syntax(checker, function.result, function.pkg, function.file)
|
||||
if diagnostic := add_unsupported_type_diagnostic(checker, function.span, result_type);
|
||||
diagnostic != source.INVALID_DIAGNOSTIC {
|
||||
@@ -3221,7 +3249,7 @@ validate_declarations :: proc(checker: ^Checker) {
|
||||
)
|
||||
}
|
||||
}
|
||||
if types.is_valid(function.error) {
|
||||
if types.is_valid(function.error) && !signature_poisoned {
|
||||
error_type := type_from_syntax(checker, function.error, function.pkg, function.file)
|
||||
error_sum := types.is_enum(error_type, &checker.module.types) ||
|
||||
types.is_tagged_union(error_type, &checker.module.types)
|
||||
@@ -3239,7 +3267,7 @@ validate_declarations :: proc(checker: ^Checker) {
|
||||
)
|
||||
}
|
||||
}
|
||||
if !function.has_body && !function.c_abi {
|
||||
if !function.has_body && !function.c_abi && !signature_poisoned {
|
||||
checker.template_diagnostics[function_id] = source.addf(
|
||||
checker.diagnostics,
|
||||
function.span,
|
||||
@@ -3247,7 +3275,7 @@ validate_declarations :: proc(checker: ^Checker) {
|
||||
symbol_text(checker, function.name),
|
||||
)
|
||||
}
|
||||
if function.variadic && (!function.c_abi || function.has_body) {
|
||||
if function.variadic && (!function.c_abi || function.has_body) && !signature_poisoned {
|
||||
checker.template_diagnostics[function_id] = source.addf(
|
||||
checker.diagnostics,
|
||||
function.span,
|
||||
@@ -3255,7 +3283,7 @@ validate_declarations :: proc(checker: ^Checker) {
|
||||
symbol_text(checker, function.name),
|
||||
)
|
||||
}
|
||||
if !function.has_body && function.c_abi {
|
||||
if !function.has_body && function.c_abi && !signature_poisoned {
|
||||
for param in function.params {
|
||||
param_type := type_from_syntax(checker, param.type, function.pkg, function.file)
|
||||
if add_unsupported_type_diagnostic(checker, param.span, param_type) !=
|
||||
@@ -3351,7 +3379,7 @@ is_inferred_record_field :: proc(checker: ^Checker, slot: int) -> bool {
|
||||
types.is_constraint(checker.record_field_constraints[slot])
|
||||
}
|
||||
|
||||
record_field_owner :: proc(checker: ^Checker, slot: int) -> (symbol.Id, symbol.Id, bool) {
|
||||
record_field_owner :: proc(checker: ^Checker, slot: int) -> (symbol.Id, symbol.Id, ast.Package_Id, bool) {
|
||||
for item in checker.module.types.nodes {
|
||||
if !(item.declared && (item.kind == .Struct || item.kind == .Union) &&
|
||||
!item.c_layout && symbol.is_valid(symbol.Id(item.name))) {
|
||||
@@ -3360,10 +3388,10 @@ record_field_owner :: proc(checker: ^Checker, slot: int) -> (symbol.Id, symbol.I
|
||||
start := int(item.field_start)
|
||||
if slot >= start && slot < start+int(item.field_count) &&
|
||||
slot >= 0 && slot < len(checker.module.types.fields) {
|
||||
return symbol.Id(item.name), symbol.Id(checker.module.types.fields[slot].name), true
|
||||
return symbol.Id(item.name), symbol.Id(checker.module.types.fields[slot].name), ast.Package_Id(item.pkg), true
|
||||
}
|
||||
}
|
||||
return symbol.INVALID, symbol.INVALID, false
|
||||
return symbol.INVALID, symbol.INVALID, ast.INVALID_PACKAGE, false
|
||||
}
|
||||
|
||||
record_field_conflict :: proc(checker: ^Checker, slot: int, actual: types.Type, span: source.Span) {
|
||||
@@ -3484,7 +3512,7 @@ finalize_record_field_inference :: proc(checker: ^Checker) {
|
||||
if !types.is_constraint(constraint) {
|
||||
continue
|
||||
}
|
||||
record_name, field_name, ok := record_field_owner(checker, slot)
|
||||
record_name, field_name, record_pkg, ok := record_field_owner(checker, slot)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
@@ -3509,7 +3537,8 @@ finalize_record_field_inference :: proc(checker: ^Checker) {
|
||||
symbol_text(checker, record_name), symbol_text(checker, field_name),
|
||||
)
|
||||
}
|
||||
} else if types.is_constraint(current) {
|
||||
} else if types.is_constraint(current) &&
|
||||
!(int(record_pkg) < len(checker.poisoned_packages) && checker.poisoned_packages[record_pkg]) {
|
||||
source.addf(
|
||||
checker.diagnostics,
|
||||
source.Span{},
|
||||
@@ -5622,6 +5651,10 @@ infer_all :: proc(checker: ^Checker) {
|
||||
// Demands accumulate in global_demands so the default never blocks a later
|
||||
// cross-family demand (e.g. integer literal -> unsigned or float).
|
||||
for global, index in checker.ast_module.globals {
|
||||
if global.diagnostic != source.INVALID_DIAGNOSTIC {
|
||||
checker.global_types[index] = types.I64
|
||||
continue
|
||||
}
|
||||
declared := resolve_inferred_array(
|
||||
checker,
|
||||
type_from_syntax(checker, global.type, global.pkg, global.file),
|
||||
@@ -5675,7 +5708,7 @@ infer_all :: proc(checker: ^Checker) {
|
||||
// Backward demands: a global pushes its own (declared or already-resolved) type
|
||||
// onto open numeric slots reachable through names and numeric arithmetic.
|
||||
for global, index in checker.ast_module.globals {
|
||||
if global.external {
|
||||
if global.external || global.diagnostic != source.INVALID_DIAGNOSTIC {
|
||||
continue
|
||||
}
|
||||
demand := checker.global_types[index]
|
||||
@@ -5689,7 +5722,7 @@ infer_all :: proc(checker: ^Checker) {
|
||||
// concrete-typed ones) for its side effect of specializing called functions and
|
||||
// recording demands from call arguments in their initializers.
|
||||
for global, index in checker.ast_module.globals {
|
||||
if global.external {
|
||||
if global.external || global.diagnostic != source.INVALID_DIAGNOSTIC {
|
||||
continue
|
||||
}
|
||||
declared := resolve_inferred_array(
|
||||
@@ -5765,7 +5798,8 @@ infer_all :: proc(checker: ^Checker) {
|
||||
// No authoritative demand can still arrive. Assign final defaults, then
|
||||
// continue the same fixpoint so dependent globals/specs observe them.
|
||||
for global, index in checker.ast_module.globals {
|
||||
if global.external || is_runtime_type(checker, checker.global_types[index]) {
|
||||
if global.external || global.diagnostic != source.INVALID_DIAGNOSTIC ||
|
||||
is_runtime_type(checker, checker.global_types[index]) {
|
||||
continue
|
||||
}
|
||||
if checker.global_open_const[index] {
|
||||
@@ -5807,7 +5841,7 @@ prune_specs :: proc(checker: ^Checker) {
|
||||
mark_spec_demanded(checker, find_spec(checker, checker.io_provider_template, nil), &stack)
|
||||
}
|
||||
for global in checker.ast_module.globals {
|
||||
if global.external {
|
||||
if global.external || global.diagnostic != source.INVALID_DIAGNOSTIC {
|
||||
continue
|
||||
}
|
||||
_ = infer_expr(checker, global.expr, nil, global.pkg, global.file, &stack)
|
||||
@@ -5859,6 +5893,23 @@ invalid_hir_expr :: proc(
|
||||
)
|
||||
}
|
||||
|
||||
invalid_expr_diagnostic :: proc(checker: ^Checker, id: hir.Expr_Id) -> (source.Diagnostic_Id, bool) {
|
||||
if id == hir.INVALID_EXPR || int(id) >= len(checker.module.exprs) {
|
||||
return source.INVALID_DIAGNOSTIC, false
|
||||
}
|
||||
expr := checker.module.exprs[id]
|
||||
return expr.diagnostic, expr.kind == .Invalid && expr.diagnostic != source.INVALID_DIAGNOSTIC
|
||||
}
|
||||
|
||||
propagate_invalid_expr :: proc(checker: ^Checker, span: source.Span, values: ..hir.Expr_Id) -> (hir.Expr_Id, bool) {
|
||||
for value in values {
|
||||
if diagnostic, invalid := invalid_expr_diagnostic(checker, value); invalid {
|
||||
return invalid_hir_expr(checker, span, diagnostic), true
|
||||
}
|
||||
}
|
||||
return hir.INVALID_EXPR, false
|
||||
}
|
||||
|
||||
add_unique_global :: proc(values: ^[dynamic]hir.Global_Id, value: hir.Global_Id) {
|
||||
for existing in values {
|
||||
if existing == value {
|
||||
@@ -5935,6 +5986,9 @@ coerce_expr :: proc(
|
||||
if expr_id == hir.INVALID_EXPR {
|
||||
return expr_id
|
||||
}
|
||||
if _, invalid := invalid_expr_diagnostic(checker, expr_id); invalid {
|
||||
return expr_id
|
||||
}
|
||||
actual := checker.module.exprs[expr_id].type
|
||||
if types.equal(actual, expected) {
|
||||
return expr_id
|
||||
@@ -6918,6 +6972,9 @@ build_compound_expr :: proc(
|
||||
})
|
||||
}
|
||||
value := build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
if invalid, propagated := propagate_invalid_expr(checker, expr.span, value); propagated {
|
||||
return invalid
|
||||
}
|
||||
if !hir_is_location(checker, value) {
|
||||
id := source.add(checker.diagnostics, expr.span, "'&' requires an addressable location")
|
||||
return invalid_hir_expr(checker, expr.span, id)
|
||||
@@ -6936,6 +6993,9 @@ build_compound_expr :: proc(
|
||||
})
|
||||
case .Deref:
|
||||
pointer := build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
if invalid, propagated := propagate_invalid_expr(checker, expr.span, pointer); propagated {
|
||||
return invalid
|
||||
}
|
||||
pointer_type := checker.module.exprs[pointer].type
|
||||
if !types.is_pointer(pointer_type, store) {
|
||||
id := source.add(checker.diagnostics, expr.span, "postfix '^' requires a pointer")
|
||||
@@ -6949,6 +7009,9 @@ build_compound_expr :: proc(
|
||||
container := build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
index := build_nested_expr(checker, expr.right, locals, global_reads, calls, types.USIZE, pkg, file)
|
||||
index = coerce_expr(checker, index, types.USIZE, expr.span)
|
||||
if invalid, propagated := propagate_invalid_expr(checker, expr.span, container, index); propagated {
|
||||
return invalid
|
||||
}
|
||||
container_type := checker.module.exprs[container].type
|
||||
item, ok := types.container(container_type, store)
|
||||
if !ok {
|
||||
@@ -6970,6 +7033,9 @@ build_compound_expr :: proc(
|
||||
})
|
||||
}
|
||||
container := build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
if invalid, propagated := propagate_invalid_expr(checker, expr.span, container); propagated {
|
||||
return invalid
|
||||
}
|
||||
container_type := checker.module.exprs[container].type
|
||||
item, ok := types.container(container_type, store)
|
||||
if !ok {
|
||||
@@ -6987,6 +7053,10 @@ build_compound_expr :: proc(
|
||||
if bound != ast.INVALID_EXPR {
|
||||
bounds[index] = build_nested_expr(checker, bound, locals, global_reads, calls, types.USIZE, pkg, file)
|
||||
bounds[index] = coerce_expr(checker, bounds[index], types.USIZE, checker.ast_module.exprs[bound].span)
|
||||
if invalid, propagated := propagate_invalid_expr(checker, expr.span, bounds[index]); propagated {
|
||||
delete(bounds, checker.allocator)
|
||||
return invalid
|
||||
}
|
||||
}
|
||||
}
|
||||
preserve_sentinel := item.has_sentinel && expr.args[1] == ast.INVALID_EXPR
|
||||
@@ -7000,11 +7070,17 @@ build_compound_expr :: proc(
|
||||
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)
|
||||
if invalid, propagated := propagate_invalid_expr(checker, expr.span, base); propagated {
|
||||
return invalid
|
||||
}
|
||||
base_type := checker.module.exprs[base].type
|
||||
result, _ := build_field_from_value(checker, expr, base, base_type)
|
||||
return result
|
||||
case .Unwrap:
|
||||
optional := build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
if invalid, propagated := propagate_invalid_expr(checker, expr.span, optional); propagated {
|
||||
return invalid
|
||||
}
|
||||
optional_type := checker.module.exprs[optional].type
|
||||
if !types.is_optional(optional_type, store) {
|
||||
id := source.add(checker.diagnostics, expr.span, "postfix '?' requires an optional")
|
||||
@@ -7016,6 +7092,9 @@ build_compound_expr :: proc(
|
||||
})
|
||||
case .Orelse:
|
||||
optional := build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
if invalid, propagated := propagate_invalid_expr(checker, expr.span, optional); propagated {
|
||||
return invalid
|
||||
}
|
||||
optional_type := checker.module.exprs[optional].type
|
||||
if !types.is_optional(optional_type, store) {
|
||||
id := source.add(checker.diagnostics, expr.span, "'orelse' requires an optional left operand")
|
||||
@@ -7035,6 +7114,9 @@ build_compound_expr :: proc(
|
||||
}
|
||||
left_expected := expected if checker.ast_module.exprs[expr.left].kind == .Call else types.INVALID
|
||||
channel := build_nested_expr(checker, expr.left, locals, global_reads, calls, left_expected, pkg, file)
|
||||
if diagnostic, invalid := invalid_expr_diagnostic(checker, channel); invalid {
|
||||
return invalid_hir_expr(checker, expr.span, diagnostic)
|
||||
}
|
||||
channel_type := checker.module.exprs[channel].type
|
||||
success := types.fallible_success(channel_type, store)
|
||||
if !types.is_valid(success) {
|
||||
@@ -7076,6 +7158,9 @@ build_compound_expr :: proc(
|
||||
case .Catch:
|
||||
left_expected := expected if checker.ast_module.exprs[expr.left].kind == .Call else types.INVALID
|
||||
channel := build_nested_expr(checker, expr.left, locals, global_reads, calls, left_expected, pkg, file)
|
||||
if diagnostic, invalid := invalid_expr_diagnostic(checker, channel); invalid {
|
||||
return invalid_hir_expr(checker, expr.span, diagnostic)
|
||||
}
|
||||
channel_type := checker.module.exprs[channel].type
|
||||
success := types.fallible_success(channel_type, store)
|
||||
if !types.is_valid(success) {
|
||||
@@ -7145,6 +7230,9 @@ build_compound_expr :: proc(
|
||||
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, types.INVALID, pkg, file)
|
||||
}
|
||||
if invalid, propagated := propagate_invalid_expr(checker, expr.span, left, right); propagated {
|
||||
return invalid
|
||||
}
|
||||
child := expected_child
|
||||
if !types.is_valid(child) {
|
||||
child = types.widest(checker.module.exprs[left].type, checker.module.exprs[right].type)
|
||||
@@ -7174,6 +7262,9 @@ build_compound_expr :: proc(
|
||||
})
|
||||
case .Not:
|
||||
operand := build_nested_expr(checker, expr.left, locals, global_reads, calls, types.BOOL, pkg, file)
|
||||
if invalid, propagated := propagate_invalid_expr(checker, expr.span, operand); propagated {
|
||||
return invalid
|
||||
}
|
||||
operand_type := checker.module.exprs[operand].type
|
||||
if checker.module.exprs[operand].kind != .Invalid && !types.is_bool(operand_type) {
|
||||
id := source.add(checker.diagnostics, expr.span, "'!' requires a bool operand")
|
||||
@@ -7186,6 +7277,9 @@ build_compound_expr :: proc(
|
||||
case .And, .Or:
|
||||
left := build_nested_expr(checker, expr.left, locals, global_reads, calls, types.BOOL, pkg, file)
|
||||
right := build_nested_expr(checker, expr.right, locals, global_reads, calls, types.BOOL, pkg, file)
|
||||
if invalid, propagated := propagate_invalid_expr(checker, expr.span, left, right); propagated {
|
||||
return invalid
|
||||
}
|
||||
left_type := checker.module.exprs[left].type
|
||||
right_type := checker.module.exprs[right].type
|
||||
left_ok := checker.module.exprs[left].kind == .Invalid || types.is_bool(left_type)
|
||||
@@ -7226,11 +7320,11 @@ build_compound_expr :: proc(
|
||||
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, types.INVALID, pkg, file)
|
||||
}
|
||||
if invalid, propagated := propagate_invalid_expr(checker, expr.span, left, right); propagated {
|
||||
return invalid
|
||||
}
|
||||
left_type := checker.module.exprs[left].type
|
||||
right_type := checker.module.exprs[right].type
|
||||
if checker.module.exprs[left].kind == .Invalid || checker.module.exprs[right].kind == .Invalid {
|
||||
return invalid_hir_expr(checker, expr.span, expr.diagnostic, types.BOOL)
|
||||
}
|
||||
operand_type := types.INVALID
|
||||
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) {
|
||||
@@ -7420,6 +7514,9 @@ build_binary_arith :: proc(
|
||||
left, right: hir.Expr_Id,
|
||||
span: source.Span,
|
||||
) -> hir.Expr_Id {
|
||||
if invalid, propagated := propagate_invalid_expr(checker, span, left, right); propagated {
|
||||
return invalid
|
||||
}
|
||||
// Pointer arithmetic is only defined for `+` (many-pointer + usize).
|
||||
if op == .Add &&
|
||||
types.is_many_pointer(checker.module.exprs[left].type, &checker.module.types) &&
|
||||
@@ -7960,6 +8057,9 @@ build_expr :: proc(
|
||||
checker.diagnostics, expr.span, message,
|
||||
symbol_text(checker, expr.name), len(function.params), len(expr.args),
|
||||
)
|
||||
if !function.imported {
|
||||
source.add_secondary_label(checker.diagnostics, id, function.span, "function declared here")
|
||||
}
|
||||
} else if len(mapping_failure) > 0 {
|
||||
id = source.addf(
|
||||
checker.diagnostics, expr.span,
|
||||
@@ -8009,6 +8109,11 @@ build_expr :: proc(
|
||||
}
|
||||
if frame.stage == 5 {
|
||||
operand := last
|
||||
if invalid, propagated := propagate_invalid_expr(checker, expr.span, operand); propagated {
|
||||
last = invalid
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
operand_type := checker.module.exprs[operand].type
|
||||
if !types.is_signed(operand_type, checker.target) && !types.is_float(operand_type, checker.target) {
|
||||
id := source.add(checker.diagnostics, expr.span, "negation requires a signed integer or float")
|
||||
@@ -8071,6 +8176,17 @@ build_expr :: proc(
|
||||
continue
|
||||
}
|
||||
}
|
||||
if invalid, propagated := propagate_invalid_expr(checker, expr.span, ..stack[frame_index].built_args); propagated {
|
||||
delete(stack[frame_index].arg_types, checker.allocator)
|
||||
stack[frame_index].arg_types = nil
|
||||
delete(stack[frame_index].built_args, checker.allocator)
|
||||
stack[frame_index].built_args = nil
|
||||
delete(stack[frame_index].mapping, checker.allocator)
|
||||
stack[frame_index].mapping = nil
|
||||
last = invalid
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
function := checker.ast_module.functions[frame.template]
|
||||
comptime_values: []Comptime_Value
|
||||
comptime_ok := false
|
||||
@@ -8236,6 +8352,11 @@ build_expr :: proc(
|
||||
}
|
||||
if frame.stage == 6 {
|
||||
callee := last
|
||||
if invalid, propagated := propagate_invalid_expr(checker, expr.span, callee); propagated {
|
||||
last = invalid
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
_, function_item, function_type, ok := types.function_pointer(checker.module.exprs[callee].type, &checker.module.types)
|
||||
if !ok {
|
||||
id := source.add(checker.diagnostics, expr.span, "call target is not a function pointer")
|
||||
@@ -8279,6 +8400,17 @@ build_expr :: proc(
|
||||
continue
|
||||
}
|
||||
}
|
||||
invalid, propagated := propagate_invalid_expr(checker, expr.span, frame.left)
|
||||
if !propagated {
|
||||
invalid, propagated = propagate_invalid_expr(checker, expr.span, ..stack[frame_index].built_args)
|
||||
}
|
||||
if propagated {
|
||||
delete(stack[frame_index].built_args, checker.allocator)
|
||||
stack[frame_index].built_args = nil
|
||||
last = invalid
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
callee_type := checker.module.exprs[frame.left].type
|
||||
_, function_item, function_type, ok := types.function_pointer(callee_type, &checker.module.types)
|
||||
if !ok {
|
||||
@@ -8897,6 +9029,22 @@ build_block :: proc(
|
||||
duplicate_start := scope_start if duplicate_scope_start < 0 else duplicate_scope_start
|
||||
for statement_id in statements {
|
||||
statement := checker.ast_module.statements[statement_id]
|
||||
if statement.diagnostic != source.INVALID_DIAGNOSTIC {
|
||||
if statement.kind == .Declaration && symbol.is_valid(statement.name) {
|
||||
if _, found := find_build_local(ctx.locals^[duplicate_start:], statement.name); !found {
|
||||
_ = append_build_local(
|
||||
ctx, statement.name, types.I64, !statement.immutable, statement.span,
|
||||
)
|
||||
}
|
||||
}
|
||||
append(&body, hir.stmt_id(len(checker.module.statements)))
|
||||
append(&checker.module.statements, hir.Stmt{
|
||||
kind = .Trap, span = statement.span, expr = hir.INVALID_EXPR,
|
||||
local = hir.INVALID_LOCAL, diagnostic = statement.diagnostic,
|
||||
})
|
||||
ctx.problematic^ = true
|
||||
continue
|
||||
}
|
||||
switch statement.kind {
|
||||
case .Declaration:
|
||||
// A value block (`x :: { ... yield v }` / `x T = { ... }`): the parser
|
||||
@@ -9070,6 +9218,15 @@ build_block :: proc(
|
||||
checker, statement.target, ctx.locals^[:], ctx.global_reads, ctx.calls,
|
||||
types.INVALID, ctx.pkg, ctx.file,
|
||||
)
|
||||
if diagnostic, invalid := invalid_expr_diagnostic(checker, target_expr); invalid {
|
||||
append(&body, hir.stmt_id(len(checker.module.statements)))
|
||||
append(&checker.module.statements, hir.Stmt{
|
||||
kind=.Trap, span=statement.span, local=hir.INVALID_LOCAL,
|
||||
target=hir.INVALID_EXPR, expr=hir.INVALID_EXPR, diagnostic=diagnostic,
|
||||
})
|
||||
ctx.problematic^ = true
|
||||
continue
|
||||
}
|
||||
target_type := checker.module.exprs[target_expr].type
|
||||
if !hir_location_writable(checker, target_expr, ctx.locals^[:]) {
|
||||
id := source.add(checker.diagnostics, statement.span, "assignment target is not writable")
|
||||
@@ -9092,6 +9249,15 @@ build_block :: proc(
|
||||
checker, statement.expr, ctx.locals^[:], ctx.global_reads, ctx.calls,
|
||||
rhs_expected, ctx.pkg, ctx.file,
|
||||
)
|
||||
if diagnostic, invalid := invalid_expr_diagnostic(checker, value); invalid {
|
||||
append(&body, hir.stmt_id(len(checker.module.statements)))
|
||||
append(&checker.module.statements, hir.Stmt{
|
||||
kind=.Trap, span=statement.span, local=hir.INVALID_LOCAL,
|
||||
target=hir.INVALID_EXPR, expr=hir.INVALID_EXPR, diagnostic=diagnostic,
|
||||
})
|
||||
ctx.problematic^ = true
|
||||
continue
|
||||
}
|
||||
if types.is_many_pointer(target_type, &checker.module.types) {
|
||||
assignment_op = .Pointer_Add
|
||||
if statement.assignment_op != .Add {
|
||||
@@ -9208,6 +9374,15 @@ build_block :: proc(
|
||||
continue
|
||||
}
|
||||
target_expr := build_global_reference(checker, global, statement.span, ctx.global_reads)
|
||||
if diagnostic, invalid := invalid_expr_diagnostic(checker, target_expr); invalid {
|
||||
append(&body, hir.stmt_id(len(checker.module.statements)))
|
||||
append(&checker.module.statements, hir.Stmt{
|
||||
kind=.Trap, span=statement.span, local=hir.INVALID_LOCAL,
|
||||
target=hir.INVALID_EXPR, expr=hir.INVALID_EXPR, diagnostic=diagnostic,
|
||||
})
|
||||
ctx.problematic^ = true
|
||||
continue
|
||||
}
|
||||
target_type := checker.module.exprs[target_expr].type
|
||||
if !hir_location_writable(checker, target_expr, ctx.locals^[:]) {
|
||||
id := source.add(checker.diagnostics, statement.span, "assignment target is not writable")
|
||||
@@ -9425,7 +9600,14 @@ build_block :: proc(
|
||||
})
|
||||
case .Expression:
|
||||
value := build_expr(checker, statement.expr, ctx.locals^[:], ctx.global_reads, ctx.calls, types.INVALID, ctx.pkg, ctx.file)
|
||||
if !types.is_void(checker.module.exprs[value].type) {
|
||||
if diagnostic, invalid := invalid_expr_diagnostic(checker, value); invalid {
|
||||
append(&body, hir.stmt_id(len(checker.module.statements)))
|
||||
append(&checker.module.statements, hir.Stmt{
|
||||
kind = .Trap, span = statement.span, expr = hir.INVALID_EXPR,
|
||||
local = hir.INVALID_LOCAL, diagnostic = diagnostic,
|
||||
})
|
||||
ctx.problematic^ = true
|
||||
} else if !types.is_void(checker.module.exprs[value].type) {
|
||||
id := source.add(checker.diagnostics, statement.span, "non-void expression result must be consumed or assigned to '_'")
|
||||
append(&body, hir.stmt_id(len(checker.module.statements)))
|
||||
append(&checker.module.statements, hir.Stmt{
|
||||
@@ -11504,15 +11686,10 @@ build_function :: proc(checker: ^Checker, id: Spec_Id) {
|
||||
checker.current_comptime_values = spec.comptime_values
|
||||
defer checker.current_comptime_values = previous_comptime
|
||||
signature_diagnostic := source.INVALID_DIAGNOSTIC
|
||||
if !types.is_void(spec.result) && !is_runtime_type(checker, spec.result) {
|
||||
unresolved_result := !types.is_void(spec.result) && !is_runtime_type(checker, spec.result)
|
||||
if unresolved_result {
|
||||
checker.specs[id].result = types.I64
|
||||
spec.result = types.I64
|
||||
signature_diagnostic = source.addf(
|
||||
checker.diagnostics,
|
||||
function.span,
|
||||
"could not resolve a concrete result type for '%s'",
|
||||
symbol_text(checker, function.name),
|
||||
)
|
||||
}
|
||||
for arg in spec.args {
|
||||
if !is_runtime_type(checker, arg) {
|
||||
@@ -11582,6 +11759,16 @@ build_function :: proc(checker: ^Checker, id: Spec_Id) {
|
||||
checker.template_diagnostics[spec.template] != source.INVALID_DIAGNOSTIC
|
||||
native_main := function.pkg == 0 && function.name == checker.main_symbol && checker.entry_point == .Plain
|
||||
if !function.has_body {
|
||||
if unresolved_result && signature_diagnostic == source.INVALID_DIAGNOSTIC &&
|
||||
checker.template_diagnostics[spec.template] == source.INVALID_DIAGNOSTIC {
|
||||
signature_diagnostic = source.addf(
|
||||
checker.diagnostics,
|
||||
function.span,
|
||||
"could not resolve a concrete result type for '%s'",
|
||||
symbol_text(checker, function.name),
|
||||
)
|
||||
problematic = true
|
||||
}
|
||||
assert(spec.hir_id == hir.function_id(len(checker.module.functions)))
|
||||
append(
|
||||
&checker.module.functions,
|
||||
@@ -11665,6 +11852,22 @@ build_function :: proc(checker: ^Checker, id: Spec_Id) {
|
||||
append(&body, block_stmt)
|
||||
}
|
||||
delete(block, checker.allocator)
|
||||
if unresolved_result && signature_diagnostic == source.INVALID_DIAGNOSTIC &&
|
||||
checker.template_diagnostics[spec.template] == source.INVALID_DIAGNOSTIC && !problematic {
|
||||
signature_diagnostic = source.addf(
|
||||
checker.diagnostics,
|
||||
function.span,
|
||||
"could not resolve a concrete result type for '%s'",
|
||||
symbol_text(checker, function.name),
|
||||
)
|
||||
append(&body, hir.stmt_id(len(checker.module.statements)))
|
||||
append(&checker.module.statements, hir.Stmt{
|
||||
kind=.Trap, span=function.span, expr=hir.INVALID_EXPR,
|
||||
local=hir.INVALID_LOCAL, diagnostic=signature_diagnostic,
|
||||
})
|
||||
problematic = true
|
||||
returns = true
|
||||
}
|
||||
|
||||
fallible_void := types.kind(spec.result, &checker.module.types) == .Fallible &&
|
||||
types.is_void(types.fallible_success(spec.result, &checker.module.types))
|
||||
@@ -11773,6 +11976,28 @@ static_integer_value :: proc(module: ^hir.Module, expr_id: hir.Expr_Id) -> (i64,
|
||||
|
||||
build_globals :: proc(checker: ^Checker) {
|
||||
for global, global_index in checker.ast_module.globals {
|
||||
if global.diagnostic != source.INVALID_DIAGNOSTIC {
|
||||
global_type := checker.global_types[global_index]
|
||||
if !is_runtime_type(checker, global_type) {
|
||||
global_type = types.I64
|
||||
}
|
||||
expr := hir.INVALID_EXPR
|
||||
if !global.external {
|
||||
expr = invalid_hir_expr(checker, global.span, global.diagnostic, global_type)
|
||||
}
|
||||
append(&checker.module.globals, hir.Global{
|
||||
name=global.name,
|
||||
link_name=strings.clone(global.link_name, checker.allocator),
|
||||
type=global_type,
|
||||
expr=expr,
|
||||
external=global.external,
|
||||
writable=global.writable || !global.immutable,
|
||||
direct_problem=true,
|
||||
problematic=true,
|
||||
diagnostic=global.diagnostic,
|
||||
})
|
||||
continue
|
||||
}
|
||||
if global.external {
|
||||
global_type := checker.global_types[global_index]
|
||||
writable := global.writable
|
||||
@@ -11841,6 +12066,12 @@ build_globals :: proc(checker: ^Checker) {
|
||||
global_type = checker.module.exprs[expr].type
|
||||
}
|
||||
diagnostic := source.INVALID_DIAGNOSTIC
|
||||
if root, invalid := invalid_expr_diagnostic(checker, expr); invalid {
|
||||
diagnostic = root
|
||||
if !is_runtime_type(checker, global_type) {
|
||||
global_type = types.I64
|
||||
}
|
||||
}
|
||||
if !global.immutable && is_undefined_expr(checker, global.expr) {
|
||||
diagnostic = source.add(
|
||||
checker.diagnostics,
|
||||
@@ -12188,6 +12419,7 @@ check :: proc(
|
||||
checker.record_field_defaults = make([]types.Type, len(checker.module.types.fields), allocator)
|
||||
checker.record_field_conflicts = make([]types.Type, len(checker.module.types.fields), allocator)
|
||||
checker.record_field_conflict_spans = make([]source.Span, len(checker.module.types.fields), allocator)
|
||||
checker.poisoned_packages = make([]bool, max(len(ast_module.packages), 1), allocator)
|
||||
init_record_field_inference(&checker)
|
||||
checker.external_global_canonical = make([]ast.Global_Id, len(ast_module.globals), allocator)
|
||||
checker.external_global_diagnostics = make([]source.Diagnostic_Id, len(ast_module.globals), allocator)
|
||||
@@ -12199,8 +12431,17 @@ check :: proc(
|
||||
}
|
||||
checker.constants = make([]Constant, len(ast_module.exprs), allocator)
|
||||
checker.template_diagnostics = make([]source.Diagnostic_Id, len(ast_module.functions), allocator)
|
||||
for &diagnostic in checker.template_diagnostics {
|
||||
diagnostic = source.INVALID_DIAGNOSTIC
|
||||
for &diagnostic, index in checker.template_diagnostics {
|
||||
diagnostic = ast_module.functions[index].diagnostic
|
||||
pkg := ast_module.functions[index].pkg
|
||||
if diagnostic != source.INVALID_DIAGNOSTIC && int(pkg) < len(checker.poisoned_packages) {
|
||||
checker.poisoned_packages[pkg] = true
|
||||
}
|
||||
}
|
||||
for global in ast_module.globals {
|
||||
if global.diagnostic != source.INVALID_DIAGNOSTIC && int(global.pkg) < len(checker.poisoned_packages) {
|
||||
checker.poisoned_packages[global.pkg] = true
|
||||
}
|
||||
}
|
||||
defer {
|
||||
for spec in checker.specs {
|
||||
@@ -12220,6 +12461,7 @@ check :: proc(
|
||||
delete(checker.record_field_defaults, allocator)
|
||||
delete(checker.record_field_conflicts, allocator)
|
||||
delete(checker.record_field_conflict_spans, allocator)
|
||||
delete(checker.poisoned_packages, allocator)
|
||||
delete(checker.external_global_canonical, allocator)
|
||||
delete(checker.external_global_diagnostics, allocator)
|
||||
delete(checker.constants, allocator)
|
||||
|
||||
Reference in New Issue
Block a user