broaden type inference from context (assignment statements)
This commit is contained in:
@@ -253,7 +253,7 @@
|
|||||||
```
|
```
|
||||||
|
|
||||||
17. for if statements, allow `if (cond) one-line statement` (instead of forcing either `if (cond) { block }` or `if cond { block }`)
|
17. for if statements, allow `if (cond) one-line statement` (instead of forcing either `if (cond) { block }` or `if cond { block }`)
|
||||||
- if statements without a bracketed body must enclose the condition in parentheses
|
- if statements without a bracketed body must wrap the condition in parentheses UNLESS it's a function call
|
||||||
|
|
||||||
18. add `defer` statement (inspired by zig)
|
18. add `defer` statement (inspired by zig)
|
||||||
|
|
||||||
|
|||||||
@@ -1727,7 +1727,7 @@ infer_statements :: proc(
|
|||||||
case .Assignment:
|
case .Assignment:
|
||||||
value_type := infer_expr(checker, statement.expr, locals^[:], pkg, file, demanded, local_types)
|
value_type := infer_expr(checker, statement.expr, locals^[:], pkg, file, demanded, local_types)
|
||||||
if statement.target != ast.INVALID_EXPR {
|
if statement.target != ast.INVALID_EXPR {
|
||||||
_ = infer_expr(checker, statement.target, locals^[:], pkg, file, demanded, local_types)
|
target_type := infer_expr(checker, statement.target, locals^[:], pkg, file, demanded, local_types)
|
||||||
target_expr := checker.ast_module.exprs[statement.target]
|
target_expr := checker.ast_module.exprs[statement.target]
|
||||||
if target_expr.kind == .Name && !symbol.is_valid(target_expr.qualifier) {
|
if target_expr.kind == .Name && !symbol.is_valid(target_expr.qualifier) {
|
||||||
if local_index, ok := find_infer_local_index(locals^[:], target_expr.name); ok &&
|
if local_index, ok := find_infer_local_index(locals^[:], target_expr.name); ok &&
|
||||||
@@ -1735,10 +1735,16 @@ infer_statements :: proc(
|
|||||||
_ = merge_infer_local_type(checker, &locals^[local_index], value_type, local_types)
|
_ = merge_infer_local_type(checker, &locals^[local_index], value_type, local_types)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Push the target's concrete type backward onto the RHS so a const used
|
||||||
|
// only in an assignment (e.g. `x += speed`) resolves, mirroring how
|
||||||
|
// declarations and returns demand their context (record_demand self-gates
|
||||||
|
// on a concrete demand and only touches fitting open slots).
|
||||||
|
_ = record_demand(checker, statement.expr, target_type, locals^[:], local_types, pkg, file)
|
||||||
} else if statement.name != checker.sink_symbol {
|
} else if statement.name != checker.sink_symbol {
|
||||||
if local_index, ok := find_infer_local_index(locals^[:], statement.name); ok &&
|
if local_index, ok := find_infer_local_index(locals^[:], statement.name); ok &&
|
||||||
locals^[local_index].mutable {
|
locals^[local_index].mutable {
|
||||||
_ = merge_infer_local_type(checker, &locals^[local_index], value_type, local_types)
|
_ = merge_infer_local_type(checker, &locals^[local_index], value_type, local_types)
|
||||||
|
_ = record_demand(checker, statement.expr, locals^[local_index].type, locals^[:], local_types, pkg, file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case .Expression:
|
case .Expression:
|
||||||
|
|||||||
@@ -6603,6 +6603,33 @@ main :: func() void {
|
|||||||
testing.expect(t, types.equal(hir_module.globals[0].type, types.U16))
|
testing.expect(t, types.equal(hir_module.globals[0].type, types.U16))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
contextual_inference_flows_through_compound_assignment :: proc(t: ^testing.T) {
|
||||||
|
text := `main :: func() void {
|
||||||
|
s :: 5
|
||||||
|
v u16 = 0
|
||||||
|
v += s
|
||||||
|
_ = v
|
||||||
|
}
|
||||||
|
`
|
||||||
|
source_file := source.Source{path="test.bro", text=text}
|
||||||
|
diagnostics := source.init_diagnostics(&source_file)
|
||||||
|
defer source.destroy_diagnostics(&diagnostics)
|
||||||
|
symbols := symbol.init_table()
|
||||||
|
defer symbol.destroy_table(&symbols)
|
||||||
|
stream := lexer.lex(&source_file, &diagnostics, &symbols)
|
||||||
|
defer delete(stream.items)
|
||||||
|
ast_module := parser.parse(&stream, &source_file, &diagnostics)
|
||||||
|
defer ast.destroy_module(&ast_module)
|
||||||
|
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||||
|
defer hir.destroy_module(&hir_module)
|
||||||
|
|
||||||
|
// `s` is used only as the RHS of `v += s`. The assignment target's type (u16) is
|
||||||
|
// demanded backward onto `s`, resolving the open constant; without it the compound
|
||||||
|
// assignment would report "arithmetic requires compatible numeric operands".
|
||||||
|
testing.expect_value(t, len(diagnostics.items), 0)
|
||||||
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
contextual_inference_rejects_local_constant_that_does_not_fit :: proc(t: ^testing.T) {
|
contextual_inference_rejects_local_constant_that_does_not_fit :: proc(t: ^testing.T) {
|
||||||
text := `main :: func() void {
|
text := `main :: func() void {
|
||||||
|
|||||||
Reference in New Issue
Block a user