add noreturn and unreachable
This commit is contained in:
+100
-29
@@ -394,7 +394,7 @@ block_reads_name :: proc(checker: ^Checker, statements: []ast.Stmt_Id, name: sym
|
||||
case .Add, .Sub, .Mul, .Div, .Bit_And, .Bit_Or, .Bit_Xor, .Shift_Left, .Shift_Right,
|
||||
.Shift_Left_Saturating, .Index, .Orelse, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or, .Range:
|
||||
append(&expr_stack, expr.left, expr.right)
|
||||
case .Invalid, .Integer, .Float, .String, .Bool, .Null, .Undefined, .Inference_Hole,
|
||||
case .Invalid, .Integer, .Float, .String, .Bool, .Null, .Unreachable, .Undefined, .Inference_Hole,
|
||||
.Type, .Name, .Function_Literal, .Anonymous_Struct_Type:
|
||||
}
|
||||
}
|
||||
@@ -518,7 +518,7 @@ write_type_label :: proc(checker: ^Checker, builder: ^strings.Builder, value: ty
|
||||
strings.write_string(builder, "enum")
|
||||
case .Alias, .Distinct, .Named:
|
||||
write_type_label(checker, builder, item.child)
|
||||
case .Invalid, .Void, .Anyopaque, .Int_Constraint, .Uint_Constraint, .Float_Constraint, .Range_Constraint, .Scalar:
|
||||
case .Invalid, .Void, .Noreturn, .Anyopaque, .Int_Constraint, .Uint_Constraint, .Float_Constraint, .Range_Constraint, .Scalar:
|
||||
strings.write_string(builder, types.name(value))
|
||||
}
|
||||
}
|
||||
@@ -2219,7 +2219,8 @@ call_mapping_semantically_valid :: proc(
|
||||
}
|
||||
if is_runtime_type(checker, expected) {
|
||||
result := function_channel_type(checker, function)
|
||||
if !is_runtime_type(checker, result) || !can_implicitly_convert_type(checker, result, expected) {
|
||||
if !types.is_noreturn(result) && !is_runtime_type(checker, result) ||
|
||||
!can_implicitly_convert_type(checker, result, expected) {
|
||||
return false, fmt.aprintf(
|
||||
"result type %s cannot convert to expected type %s",
|
||||
type_label(checker, result), type_label(checker, expected),
|
||||
@@ -3365,7 +3366,7 @@ function_value_signature :: proc(
|
||||
return nil, types.INVALID, false
|
||||
}
|
||||
result = function_channel_type(checker, function)
|
||||
if !types.is_void(result) && !is_runtime_type(checker, result) {
|
||||
if !types.is_void(result) && !types.is_noreturn(result) && !is_runtime_type(checker, result) {
|
||||
return nil, types.INVALID, false
|
||||
}
|
||||
params = make([]types.Type, len(function.params), checker.allocator)
|
||||
@@ -3402,6 +3403,9 @@ function_type_for_template :: proc(
|
||||
}
|
||||
} else {
|
||||
spec = find_spec(checker, template, params)
|
||||
if spec == INVALID_SPEC {
|
||||
spec = ensure_spec(checker, template, params)
|
||||
}
|
||||
mark_spec_demanded(checker, spec, demanded)
|
||||
}
|
||||
return function_type, spec, spec != INVALID_SPEC || !demand_spec
|
||||
@@ -3506,7 +3510,7 @@ mark_expr_imports_used :: proc(checker: ^Checker, expr_id: ast.Expr_Id, file: as
|
||||
case .Add, .Sub, .Mul, .Div, .Bit_And, .Bit_Or, .Bit_Xor, .Shift_Left, .Shift_Right,
|
||||
.Shift_Left_Saturating, .Index, .Orelse, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or, .Range:
|
||||
append(&stack, expr.left, expr.right)
|
||||
case .Invalid, .Integer, .Float, .String, .Bool, .Null, .Undefined, .Inference_Hole, .Type, .Name, .Anonymous_Struct_Type:
|
||||
case .Invalid, .Integer, .Float, .String, .Bool, .Null, .Unreachable, .Undefined, .Inference_Hole, .Type, .Name, .Anonymous_Struct_Type:
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3695,6 +3699,13 @@ validate_declarations :: proc(checker: ^Checker) {
|
||||
"void is only valid as a function result type",
|
||||
)
|
||||
}
|
||||
if !param.comptime_value && types.is_noreturn(param_type) {
|
||||
checker.template_diagnostics[function_id] = source.add(
|
||||
checker.diagnostics,
|
||||
param.span,
|
||||
"noreturn is only valid as a native function result type",
|
||||
)
|
||||
}
|
||||
if param.name != checker.sink_symbol && contains_name(locals[:], param.name) {
|
||||
source.addf(
|
||||
checker.diagnostics,
|
||||
@@ -3720,6 +3731,13 @@ validate_declarations :: proc(checker: ^Checker) {
|
||||
}
|
||||
if !has_comptime && !signature_poisoned {
|
||||
result_type := type_from_syntax(checker, function.result, function.pkg, function.file)
|
||||
if function.c_abi && types.is_noreturn(result_type) {
|
||||
checker.template_diagnostics[function_id] = source.add(
|
||||
checker.diagnostics,
|
||||
function.span,
|
||||
"noreturn is not supported across the C ABI",
|
||||
)
|
||||
}
|
||||
if diagnostic := add_unsupported_type_diagnostic(checker, function.span, result_type);
|
||||
diagnostic != source.INVALID_DIAGNOSTIC {
|
||||
checker.template_diagnostics[function_id] = diagnostic
|
||||
@@ -3788,6 +3806,7 @@ validate_declarations :: proc(checker: ^Checker) {
|
||||
}
|
||||
result := type_from_syntax(checker, function.result, function.pkg, function.file)
|
||||
if add_unsupported_type_diagnostic(checker, function.span, result) == source.INVALID_DIAGNOSTIC &&
|
||||
!types.is_noreturn(result) &&
|
||||
!types.contains_c_struct_by_value(result, &checker.module.types) &&
|
||||
!types.is_c_signature_type(result, &checker.module.types, true) {
|
||||
checker.template_diagnostics[function_id] = source.addf(
|
||||
@@ -4116,7 +4135,7 @@ validate_meta_schema :: proc(checker: ^Checker) {
|
||||
type_item, type_ok := types.node(&checker.module.types, type_info)
|
||||
type_fields := types.fields_for(&checker.module.types, type_info)
|
||||
expected_tags := []string{
|
||||
"invalid", "void", "anyopaque", "bool", "integer", "float", "array", "pointer", "slice",
|
||||
"invalid", "void", "noreturn", "anyopaque", "bool", "integer", "float", "array", "pointer", "slice",
|
||||
"range", "optional", "function", "enum", "record", "union", "fallible", "distinct",
|
||||
}
|
||||
valid = valid && type_ok && type_item.kind == .Union &&
|
||||
@@ -4308,8 +4327,8 @@ validate_type_nodes :: proc(checker: ^Checker) {
|
||||
source.add(checker.diagnostics, source.Span{}, "native function pointer parameters must be concrete runtime types")
|
||||
}
|
||||
}
|
||||
if !types.is_void(item.child) && !is_runtime_type(checker, item.child) {
|
||||
source.add(checker.diagnostics, source.Span{}, "native function pointer results must be concrete runtime types or void")
|
||||
if !types.is_void(item.child) && !types.is_noreturn(item.child) && !is_runtime_type(checker, item.child) {
|
||||
source.add(checker.diagnostics, source.Span{}, "native function pointer results must be concrete runtime types, void, or noreturn")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4631,6 +4650,8 @@ infer_compound_expr :: proc(
|
||||
return types.array(store, element, u64(len(expr.args)), false)
|
||||
case .Null:
|
||||
return expected if types.is_optional(expected, store) else types.INVALID
|
||||
case .Unreachable:
|
||||
return types.NORETURN
|
||||
case .Undefined:
|
||||
return types.INVALID
|
||||
case .Enum_Literal:
|
||||
@@ -4928,7 +4949,7 @@ infer_expr :: proc(
|
||||
case .Float:
|
||||
last = types.F64
|
||||
_ = pop(&stack)
|
||||
case .String, .Array, .Null, .Undefined, .Address, .Deref, .Index, .Slice,
|
||||
case .String, .Array, .Null, .Unreachable, .Undefined, .Address, .Deref, .Index, .Slice,
|
||||
.Field, .Unwrap, .Orelse, .Try, .Catch, .Struct_Literal, .Keyed, .Enum_Literal, .Cast,
|
||||
.Comptime, .Bool, .Not, .Bit_Not, .Bit_And, .Bit_Or, .Bit_Xor, .Shift_Left,
|
||||
.Shift_Right, .Shift_Left_Saturating, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or, .Range:
|
||||
@@ -5274,7 +5295,7 @@ infer_expr :: proc(
|
||||
}
|
||||
if checker.template_diagnostics[template] != source.INVALID_DIAGNOSTIC {
|
||||
declared := function_channel_type(checker, checker.ast_module.functions[template])
|
||||
last = declared if is_runtime_type(checker, declared) || types.is_void(declared) else types.INVALID
|
||||
last = declared if is_runtime_type(checker, declared) || types.is_void(declared) || types.is_noreturn(declared) else types.INVALID
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
@@ -5415,6 +5436,9 @@ infer_expr :: proc(
|
||||
spec = ensure_spec(checker, frame.template, stack[frame_index].args, comptime_values)
|
||||
} else {
|
||||
spec = find_spec(checker, frame.template, stack[frame_index].args, comptime_values)
|
||||
if spec == INVALID_SPEC {
|
||||
spec = ensure_spec(checker, frame.template, stack[frame_index].args, comptime_values)
|
||||
}
|
||||
mark_spec_demanded(checker, spec, demanded)
|
||||
}
|
||||
if spec != INVALID_SPEC {
|
||||
@@ -5424,7 +5448,7 @@ infer_expr :: proc(
|
||||
checker.current_comptime_values = comptime_values
|
||||
declared := function_channel_type(checker, function)
|
||||
checker.current_comptime_values = previous_comptime
|
||||
last = declared if is_runtime_type(checker, declared) || types.is_void(declared) else types.INVALID
|
||||
last = declared if is_runtime_type(checker, declared) || types.is_void(declared) || types.is_noreturn(declared) else types.INVALID
|
||||
}
|
||||
} else {
|
||||
declared := function_channel_type(checker, function)
|
||||
@@ -5432,7 +5456,7 @@ infer_expr :: proc(
|
||||
!types.is_valid(function.error) {
|
||||
last = types.I32
|
||||
} else {
|
||||
last = declared if is_runtime_type(checker, declared) || types.is_void(declared) else types.INVALID
|
||||
last = declared if is_runtime_type(checker, declared) || types.is_void(declared) || types.is_noreturn(declared) else types.INVALID
|
||||
}
|
||||
}
|
||||
delete(stack[frame_index].args, checker.allocator)
|
||||
@@ -5957,7 +5981,7 @@ infer_spec_locals_and_result :: proc(
|
||||
if function.pkg == 0 && function.name == checker.main_symbol && function.result == types.INT {
|
||||
declared = types.I32
|
||||
}
|
||||
result_hint := declared if is_runtime_type(checker, declared) || declared == types.UINT else types.INVALID
|
||||
result_hint := declared if is_runtime_type(checker, declared) || types.is_noreturn(declared) || declared == types.UINT else types.INVALID
|
||||
|
||||
locals: [dynamic]Infer_Local
|
||||
locals.allocator = checker.allocator
|
||||
@@ -6746,7 +6770,8 @@ find_build_local :: proc(locals: []Build_Local, name: symbol.Id) -> (Build_Local
|
||||
|
||||
can_implicitly_convert_type :: proc(checker: ^Checker, actual, expected: types.Type) -> bool {
|
||||
store := &checker.module.types
|
||||
if types.equal(actual, expected) ||
|
||||
if types.is_noreturn(actual) ||
|
||||
types.equal(actual, expected) ||
|
||||
types.can_widen(actual, expected) ||
|
||||
types.can_coerce_c_integer(actual, expected, checker.target) ||
|
||||
types.can_coerce_c_scalar(actual, expected, checker.target) ||
|
||||
@@ -6777,6 +6802,9 @@ coerce_expr :: proc(
|
||||
return expr_id
|
||||
}
|
||||
actual := checker.module.exprs[expr_id].type
|
||||
if types.is_noreturn(actual) {
|
||||
return expr_id
|
||||
}
|
||||
if types.equal(actual, expected) {
|
||||
return expr_id
|
||||
}
|
||||
@@ -7782,6 +7810,11 @@ build_compound_expr :: proc(
|
||||
kind=.Null, span=expr.span, type=expected, target=hir.INVALID_REF,
|
||||
left=hir.INVALID_EXPR, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
case .Unreachable:
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
kind=.Unreachable, span=expr.span, type=types.NORETURN, target=hir.INVALID_REF,
|
||||
left=hir.INVALID_EXPR, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
case .Undefined:
|
||||
id := source.add(
|
||||
checker.diagnostics,
|
||||
@@ -8703,7 +8736,7 @@ build_expr :: proc(
|
||||
continue
|
||||
}
|
||||
switch expr.kind {
|
||||
case .String, .Array, .Null, .Undefined, .Address, .Deref, .Index, .Slice,
|
||||
case .String, .Array, .Null, .Unreachable, .Undefined, .Address, .Deref, .Index, .Slice,
|
||||
.Field, .Unwrap, .Orelse, .Try, .Catch, .Struct_Literal, .Keyed,
|
||||
.Bool, .Cast, .Comptime, .Not, .Bit_Not, .Bit_And, .Bit_Or, .Bit_Xor, .Shift_Left,
|
||||
.Shift_Right, .Shift_Left_Saturating, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or, .Range,
|
||||
@@ -9413,12 +9446,16 @@ build_expr :: proc(
|
||||
checker.current_comptime_values = previous_comptime
|
||||
}
|
||||
spec := INVALID_SPEC
|
||||
if comptime_ok && frame.resolution >= 0 && frame.resolution < len(checker.call_resolutions) {
|
||||
spec = find_spec(
|
||||
checker, frame.template,
|
||||
checker.call_resolutions[frame.resolution].runtime_types,
|
||||
comptime_values,
|
||||
)
|
||||
if comptime_ok {
|
||||
if frame.resolution >= 0 && frame.resolution < len(checker.call_resolutions) {
|
||||
spec = find_spec(
|
||||
checker, frame.template,
|
||||
checker.call_resolutions[frame.resolution].runtime_types,
|
||||
comptime_values,
|
||||
)
|
||||
} else {
|
||||
spec = find_spec(checker, frame.template, stack[frame_index].arg_types, comptime_values)
|
||||
}
|
||||
}
|
||||
delete(stack[frame_index].arg_types, checker.allocator)
|
||||
stack[frame_index].arg_types = nil
|
||||
@@ -10329,7 +10366,7 @@ build_block :: proc(
|
||||
value_type = checker.module.exprs[value].type
|
||||
if is_runtime_type(checker, declared) {
|
||||
value = coerce_expr(checker, value, declared, statement.span)
|
||||
value_type = checker.module.exprs[value].type
|
||||
value_type = declared
|
||||
} else if types.is_void(declared) {
|
||||
id := source.add(checker.diagnostics, statement.span, "locals cannot have type void")
|
||||
value = invalid_hir_expr(checker, statement.span, id)
|
||||
@@ -10351,6 +10388,24 @@ build_block :: proc(
|
||||
ctx.problematic^ = true
|
||||
continue
|
||||
}
|
||||
if types.is_noreturn(value_type) {
|
||||
id := source.addf(
|
||||
checker.diagnostics,
|
||||
statement.span,
|
||||
"local '%s' cannot store a noreturn value",
|
||||
symbol_text(checker, statement.name),
|
||||
)
|
||||
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=id,
|
||||
})
|
||||
if _, found := find_build_local(ctx.locals^[duplicate_start:], statement.name); !found {
|
||||
_ = append_build_local(ctx, statement.name, types.I64, !statement.immutable, statement.span)
|
||||
}
|
||||
ctx.problematic^ = true
|
||||
continue
|
||||
}
|
||||
if _, found := find_build_local(ctx.locals^[duplicate_start:], statement.name); found {
|
||||
id := source.addf(
|
||||
checker.diagnostics, statement.span,
|
||||
@@ -10816,7 +10871,8 @@ build_block :: proc(
|
||||
local = hir.INVALID_LOCAL, diagnostic = diagnostic,
|
||||
})
|
||||
ctx.problematic^ = true
|
||||
} else if !types.is_void(checker.module.exprs[value].type) {
|
||||
} else if !types.is_void(checker.module.exprs[value].type) &&
|
||||
!types.is_noreturn(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{
|
||||
@@ -10926,7 +10982,8 @@ build_block :: proc(
|
||||
if diagnostic == source.INVALID_DIAGNOSTIC {
|
||||
diagnostic = checker.module.exprs[guard].diagnostic
|
||||
}
|
||||
} else if !types.is_bool(checker.module.exprs[guard].type) {
|
||||
} else if !types.is_bool(checker.module.exprs[guard].type) &&
|
||||
!types.is_noreturn(checker.module.exprs[guard].type) {
|
||||
diagnostic = source.add(
|
||||
checker.diagnostics,
|
||||
checker.ast_module.exprs[statement.guard].span,
|
||||
@@ -10977,7 +11034,9 @@ build_block :: proc(
|
||||
continue
|
||||
}
|
||||
condition := build_expr(checker, statement.expr, ctx.locals^[:], ctx.global_reads, ctx.calls, types.BOOL, ctx.pkg, ctx.file)
|
||||
if checker.module.exprs[condition].kind != .Invalid && !types.is_bool(checker.module.exprs[condition].type) {
|
||||
if checker.module.exprs[condition].kind != .Invalid &&
|
||||
!types.is_bool(checker.module.exprs[condition].type) &&
|
||||
!types.is_noreturn(checker.module.exprs[condition].type) {
|
||||
id := source.add(checker.diagnostics, statement.span, "'if' condition must be a bool")
|
||||
condition = invalid_hir_expr(checker, statement.span, id, types.BOOL)
|
||||
ctx.problematic^ = true
|
||||
@@ -11002,7 +11061,8 @@ build_block :: proc(
|
||||
types.BOOL, ctx.pkg, ctx.file,
|
||||
)
|
||||
if checker.module.exprs[condition].kind != .Invalid &&
|
||||
!types.is_bool(checker.module.exprs[condition].type) {
|
||||
!types.is_bool(checker.module.exprs[condition].type) &&
|
||||
!types.is_noreturn(checker.module.exprs[condition].type) {
|
||||
id := source.add(checker.diagnostics, statement.span, "'while' condition must be a bool")
|
||||
condition = invalid_hir_expr(checker, statement.span, id, types.BOOL)
|
||||
ctx.problematic^ = true
|
||||
@@ -11797,7 +11857,8 @@ emit_value_if :: proc(
|
||||
guard = build_expr(checker, if_stmt.guard, ctx.locals^[:], ctx.global_reads, ctx.calls, types.BOOL, ctx.pkg, ctx.file)
|
||||
if checker.module.exprs[guard].kind == .Invalid {
|
||||
ok = false
|
||||
} else if !types.is_bool(checker.module.exprs[guard].type) {
|
||||
} else if !types.is_bool(checker.module.exprs[guard].type) &&
|
||||
!types.is_noreturn(checker.module.exprs[guard].type) {
|
||||
source.add(checker.diagnostics, checker.ast_module.exprs[if_stmt.guard].span, "'if' unwrap guard must be a bool")
|
||||
ok = false
|
||||
}
|
||||
@@ -11814,7 +11875,9 @@ emit_value_if :: proc(
|
||||
unwraps = unwrap_list[:]
|
||||
} else {
|
||||
condition = build_expr(checker, if_stmt.expr, ctx.locals^[:], ctx.global_reads, ctx.calls, types.BOOL, ctx.pkg, ctx.file)
|
||||
if checker.module.exprs[condition].kind != .Invalid && !types.is_bool(checker.module.exprs[condition].type) {
|
||||
if checker.module.exprs[condition].kind != .Invalid &&
|
||||
!types.is_bool(checker.module.exprs[condition].type) &&
|
||||
!types.is_noreturn(checker.module.exprs[condition].type) {
|
||||
id := source.add(checker.diagnostics, if_stmt.span, "'if' condition must be a bool")
|
||||
condition = invalid_hir_expr(checker, if_stmt.span, id, types.BOOL)
|
||||
ctx.problematic^ = true
|
||||
@@ -13000,6 +13063,10 @@ enum_guards_return :: proc(module: ^hir.Module, stmts: []hir.Stmt_Id, locals: []
|
||||
all_paths_return :: proc(module: ^hir.Module, stmts: []hir.Stmt_Id, locals: []hir.Local = nil) -> bool {
|
||||
for id in stmts {
|
||||
statement := module.statements[id]
|
||||
if statement.expr != hir.INVALID_EXPR && int(statement.expr) < len(module.exprs) &&
|
||||
types.is_noreturn(module.exprs[statement.expr].type) {
|
||||
return true
|
||||
}
|
||||
#partial switch statement.kind {
|
||||
case .Return, .Trap:
|
||||
return true
|
||||
@@ -13049,6 +13116,10 @@ loop_body_breaks :: proc(module: ^hir.Module, stmts: []hir.Stmt_Id) -> bool {
|
||||
all_paths_exit :: proc(module: ^hir.Module, stmts: []hir.Stmt_Id) -> bool {
|
||||
for id in stmts {
|
||||
statement := module.statements[id]
|
||||
if statement.expr != hir.INVALID_EXPR && int(statement.expr) < len(module.exprs) &&
|
||||
types.is_noreturn(module.exprs[statement.expr].type) {
|
||||
return true
|
||||
}
|
||||
#partial switch statement.kind {
|
||||
case .Return, .Trap, .Break, .Continue:
|
||||
return true
|
||||
@@ -13078,7 +13149,7 @@ 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
|
||||
unresolved_result := !types.is_void(spec.result) && !is_runtime_type(checker, spec.result)
|
||||
unresolved_result := !types.is_void(spec.result) && !types.is_noreturn(spec.result) && !is_runtime_type(checker, spec.result)
|
||||
if unresolved_result {
|
||||
if types.is_comptime_only(spec.result, &checker.module.types) {
|
||||
signature_diagnostic = source.addf(
|
||||
|
||||
Reference in New Issue
Block a user