support shorthand enums in value-if
This commit is contained in:
@@ -38,12 +38,12 @@ Infer_Local :: struct {
|
|||||||
declared: types.Type,
|
declared: types.Type,
|
||||||
statement: ast.Stmt_Id,
|
statement: ast.Stmt_Id,
|
||||||
mutable: bool,
|
mutable: bool,
|
||||||
// open_const/open_float mark a local whose initializer is an unannotated numeric
|
// Open numeric constants and value sources can adopt a backward demand from use.
|
||||||
// constant: like an open-constant global, it can adopt a backward demand from use.
|
open_const: bool,
|
||||||
open_const: bool,
|
open_float: bool,
|
||||||
open_float: bool,
|
open_value_source: bool,
|
||||||
const_value: i128,
|
const_value: i128,
|
||||||
demanded: bool,
|
demanded: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
Build_Local :: struct {
|
Build_Local :: struct {
|
||||||
@@ -5125,6 +5125,9 @@ infer_compound_expr :: proc(
|
|||||||
}
|
}
|
||||||
field_expected := field.type if is_runtime_type(checker, field.type) else types.INVALID
|
field_expected := field.type if is_runtime_type(checker, field.type) else types.INVALID
|
||||||
actual := infer_nested_expr(checker, keyed_expr.left, locals, pkg, file, demanded, local_types, field_expected)
|
actual := infer_nested_expr(checker, keyed_expr.left, locals, pkg, file, demanded, local_types, field_expected)
|
||||||
|
_ = record_demand(
|
||||||
|
checker, keyed_expr.left, field_expected, locals, local_types, pkg, file,
|
||||||
|
)
|
||||||
if !is_inferred_record_field(checker, slot) {
|
if !is_inferred_record_field(checker, slot) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -5960,7 +5963,7 @@ infer_statements :: proc(
|
|||||||
block_type := declared_block if is_runtime_type(checker, declared_block) else types.INVALID
|
block_type := declared_block if is_runtime_type(checker, declared_block) else types.INVALID
|
||||||
local := Infer_Local{
|
local := Infer_Local{
|
||||||
name=statement.name, type=block_type, declared=declared_block,
|
name=statement.name, type=block_type, declared=declared_block,
|
||||||
statement=statement_id, mutable=!statement.immutable,
|
statement=statement_id, mutable=!statement.immutable, open_value_source=true,
|
||||||
}
|
}
|
||||||
append(locals, local)
|
append(locals, local)
|
||||||
record_infer_local_type(local, local_types)
|
record_infer_local_type(local, local_types)
|
||||||
@@ -6488,10 +6491,12 @@ merge_global_demand :: proc(checker: ^Checker, global: ast.Global_Id, demand: ty
|
|||||||
return changed
|
return changed
|
||||||
}
|
}
|
||||||
|
|
||||||
// merge_local_demand records a concrete numeric demand onto an open-constant local.
|
// merge_local_demand records a concrete demand onto an unresolved value source or
|
||||||
// The first demand replaces the literal's default type; later demands may only widen
|
// open numeric constant. Numeric demands retain their literal-family restrictions.
|
||||||
// within the chosen family.
|
|
||||||
merge_local_demand :: proc(checker: ^Checker, local: ^Infer_Local, demand: types.Type, local_types: []types.Type) -> bool {
|
merge_local_demand :: proc(checker: ^Checker, local: ^Infer_Local, demand: types.Type, local_types: []types.Type) -> bool {
|
||||||
|
if local.open_value_source {
|
||||||
|
return merge_infer_local_type(checker, local, demand, local_types)
|
||||||
|
}
|
||||||
if !(local.open_const && open_integer_accepts_demand(checker, local.const_value, demand) ||
|
if !(local.open_const && open_integer_accepts_demand(checker, local.const_value, demand) ||
|
||||||
local.open_float && open_float_accepts_demand(checker, demand)) {
|
local.open_float && open_float_accepts_demand(checker, demand)) {
|
||||||
return false
|
return false
|
||||||
@@ -10924,10 +10929,12 @@ build_block :: proc(
|
|||||||
// leaves `expr` invalid and stashes the block in `body`. Build it, then
|
// leaves `expr` invalid and stashes the block in `body`. Build it, then
|
||||||
// declare the local from the yielded value (its type for an untyped `::`).
|
// declare the local from the yielded value (its type for an untyped `::`).
|
||||||
if statement.expr == ast.INVALID_EXPR {
|
if statement.expr == ast.INVALID_EXPR {
|
||||||
expected := types.INVALID
|
declared := type_from_syntax(checker, statement.type, ctx.pkg, ctx.file)
|
||||||
typed := is_runtime_type(checker, type_from_syntax(checker, statement.type, ctx.pkg, ctx.file))
|
expected := declared if is_runtime_type(checker, declared) else types.INVALID
|
||||||
if typed {
|
if !is_runtime_type(checker, expected) &&
|
||||||
expected = type_from_syntax(checker, statement.type, ctx.pkg, ctx.file)
|
int(statement_id) < len(ctx.local_types) &&
|
||||||
|
is_runtime_type(checker, ctx.local_types[statement_id]) {
|
||||||
|
expected = ctx.local_types[statement_id]
|
||||||
}
|
}
|
||||||
value, value_type := build_value_source(ctx, &body, statement.body, expected, statement.span, statement.label, statement.value_control_flow)
|
value, value_type := build_value_source(ctx, &body, statement.body, expected, statement.span, statement.label, statement.value_control_flow)
|
||||||
if _, found := find_build_local(ctx.locals^[duplicate_start:], statement.name); found {
|
if _, found := find_build_local(ctx.locals^[duplicate_start:], statement.name); found {
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ Parser :: struct {
|
|||||||
// At the top level of if/for headers, `|` begins captures. Bitwise OR in
|
// At the top level of if/for headers, `|` begins captures. Bitwise OR in
|
||||||
// those headers remains available inside parentheses.
|
// those headers remains available inside parentheses.
|
||||||
capture_pipe: bool,
|
capture_pipe: bool,
|
||||||
|
// A parenthesized `if` condition ends before a leading-dot brace-less body.
|
||||||
|
if_condition: bool,
|
||||||
hidden_names: [dynamic]symbol.Id,
|
hidden_names: [dynamic]symbol.Id,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1331,6 +1333,10 @@ parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int
|
|||||||
})
|
})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if parser.if_condition && parser.delimiter_depth == 0 &&
|
||||||
|
parser.module.exprs[left].parenthesized && current(parser).kind == .Dot {
|
||||||
|
break
|
||||||
|
}
|
||||||
if current(parser).kind == .Dot {
|
if current(parser).kind == .Dot {
|
||||||
advance(parser)
|
advance(parser)
|
||||||
if current(parser).kind == .Integer {
|
if current(parser).kind == .Integer {
|
||||||
@@ -2160,8 +2166,11 @@ parse_if :: proc(parser: ^Parser) -> ast.Stmt_Id {
|
|||||||
saved := parser.no_struct_literal
|
saved := parser.no_struct_literal
|
||||||
parser.no_struct_literal = true
|
parser.no_struct_literal = true
|
||||||
saved_capture_pipe := parser.capture_pipe
|
saved_capture_pipe := parser.capture_pipe
|
||||||
|
saved_if_condition := parser.if_condition
|
||||||
parser.capture_pipe = true
|
parser.capture_pipe = true
|
||||||
|
parser.if_condition = true
|
||||||
condition := parse_expression(parser)
|
condition := parse_expression(parser)
|
||||||
|
parser.if_condition = saved_if_condition
|
||||||
parser.capture_pipe = saved_capture_pipe
|
parser.capture_pipe = saved_capture_pipe
|
||||||
parser.no_struct_literal = saved
|
parser.no_struct_literal = saved
|
||||||
captures: [dynamic]symbol.Id
|
captures: [dynamic]symbol.Id
|
||||||
|
|||||||
@@ -10812,6 +10812,33 @@ main func() void {
|
|||||||
testing.expect_value(t, module.statements[module.functions[0].body[1]].kind, ast.Stmt_Kind.If)
|
testing.expect_value(t, module.statements[module.functions[0].body[1]].kind, ast.Stmt_Kind.If)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
braceless_value_if_infers_shorthand_enum_from_use :: proc(t: ^testing.T) {
|
||||||
|
directory := "/tmp/brolang-test-value-if-enum"
|
||||||
|
main_path := "/tmp/brolang-test-value-if-enum/main.bro"
|
||||||
|
output := "/tmp/brolang-test-value-if-enum-output"
|
||||||
|
text := `Kind :: enum { first, second }
|
||||||
|
Box :: struct { kind Kind }
|
||||||
|
choose func(first bool) Kind {
|
||||||
|
kind :: if (first) .first else .second
|
||||||
|
return Box{kind = kind}.kind
|
||||||
|
}
|
||||||
|
main func() i32 {
|
||||||
|
if (choose(true) != .first) return 1
|
||||||
|
if (choose(false) != .second) return 2
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
`
|
||||||
|
_ = os2.remove_all(directory)
|
||||||
|
defer _ = os2.remove_all(directory)
|
||||||
|
defer _ = os.remove(output)
|
||||||
|
testing.expect(t, os.make_directory(directory) == nil)
|
||||||
|
testing.expect(t, os.write_entire_file(main_path, transmute([]byte)text))
|
||||||
|
testing.expect_value(t, compiler_core.compile_package(directory, output), 0)
|
||||||
|
state := run_executable(output)
|
||||||
|
testing.expect_value(t, state.exit_code, 0)
|
||||||
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
parser_accepts_braceless_while_bodies :: proc(t: ^testing.T) {
|
parser_accepts_braceless_while_bodies :: proc(t: ^testing.T) {
|
||||||
text := `ready func() bool { return false }
|
text := `ready func() bool { return false }
|
||||||
|
|||||||
Reference in New Issue
Block a user