array size inference from value
This commit is contained in:
+1
-1
@@ -22,7 +22,7 @@ roadmap and milestone history.
|
||||
- target-dependent C scalar primitives from `c_char` through `c_longdouble`, kept semantically distinct from native scalars
|
||||
- contextual integer/float/character literals, backward type-demand inference through names and arithmetic, and compile-time folding for numeric constant expressions
|
||||
- strict numeric conversion by default, widening where valid, C scalar coercions at C boundaries, and explicit scalar keyword casts such as `i32(x)` / `c_float(x)`
|
||||
- arrays `[N]T`, sentinel arrays `[N;S]T`, compile-time expression array counts, slices `[]T` / `[;S]T`, single-item pointers `@T`, many-item pointers `*T`, and sentinel many-item pointers `[*;S]T`
|
||||
- arrays `[N]T`, inferred-count arrays `[_]T`, sentinel arrays `[N;S]T`, compile-time expression array counts, slices `[]T` / `[;S]T`, single-item pointers `@T`, many-item pointers `*T`, and sentinel many-item pointers `[*;S]T`
|
||||
- pointer mutability via `mut`, optional pointers as nullable pointers, pointer arithmetic for many-item pointers, postfix dereference `^`, and trapping optional unwrap `?`
|
||||
- pointer-to-array `.len`, indexing, slicing, `.ptr` on slices and pointers-to-arrays, implicit address-taking for array-variable slices, and pointer/slice sentinel weakening
|
||||
- UTF-8 string literals as immutable pointers to static zero-terminated byte arrays, plus raw backtick multiline strings
|
||||
|
||||
@@ -607,6 +607,25 @@ resolve_inferred_array :: proc(checker: ^Checker, value: types.Type, expr_id: as
|
||||
return types.with_array_count(&checker.module.types, value, u64(len(expr.args)))
|
||||
}
|
||||
|
||||
has_inferred_array_count :: proc(checker: ^Checker, value: types.Type) -> bool {
|
||||
item, ok := types.node(&checker.module.types, value)
|
||||
return ok && item.kind == .Array && item.inferred_count
|
||||
}
|
||||
|
||||
resolve_inferred_array_from_type :: proc(checker: ^Checker, value, inferred: types.Type) -> types.Type {
|
||||
item, ok := types.node(&checker.module.types, value)
|
||||
actual, actual_ok := types.node(&checker.module.types, inferred)
|
||||
if !ok || !actual_ok || item.kind != .Array || actual.kind != .Array || !item.inferred_count {
|
||||
return value
|
||||
}
|
||||
if item.child != actual.child || item.mutable != actual.mutable ||
|
||||
item.has_sentinel != actual.has_sentinel ||
|
||||
(item.has_sentinel && item.sentinel != actual.sentinel) {
|
||||
return value
|
||||
}
|
||||
return types.with_array_count(&checker.module.types, value, actual.count)
|
||||
}
|
||||
|
||||
function_index_less :: proc(left, right: Function_Index_Entry) -> bool {
|
||||
if left.scope != right.scope {
|
||||
return left.scope < right.scope
|
||||
@@ -2249,8 +2268,9 @@ infer_statements :: proc(
|
||||
value_type := types.INVALID
|
||||
if !is_undefined_expr(checker, statement.expr) {
|
||||
value_type = infer_expr(checker, statement.expr, locals^[:], pkg, file, demanded, local_types)
|
||||
declared_local = resolve_inferred_array_from_type(checker, declared_local, value_type)
|
||||
}
|
||||
if is_runtime_type(checker, declared_local) {
|
||||
if is_runtime_type(checker, declared_local) && !has_inferred_array_count(checker, declared_local) {
|
||||
value_type = declared_local
|
||||
} else if types.is_constraint(declared_local) {
|
||||
// Seed the binding in-family (INVALID on mismatch, which
|
||||
@@ -2823,8 +2843,12 @@ 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 {
|
||||
declared := type_from_syntax(checker, global.type, global.pkg, global.file)
|
||||
if is_runtime_type(checker, declared) {
|
||||
declared := resolve_inferred_array(
|
||||
checker,
|
||||
type_from_syntax(checker, global.type, global.pkg, global.file),
|
||||
global.expr,
|
||||
)
|
||||
if is_runtime_type(checker, declared) && !has_inferred_array_count(checker, declared) {
|
||||
checker.global_types[index] = declared
|
||||
continue
|
||||
}
|
||||
@@ -2871,7 +2895,20 @@ infer_all :: proc(checker: ^Checker) {
|
||||
continue
|
||||
}
|
||||
inferred := infer_expr(checker, global.expr, nil, global.pkg, global.file)
|
||||
if is_runtime_type(checker, type_from_syntax(checker, global.type, global.pkg, global.file)) {
|
||||
declared := resolve_inferred_array(
|
||||
checker,
|
||||
type_from_syntax(checker, global.type, global.pkg, global.file),
|
||||
global.expr,
|
||||
)
|
||||
if resolved := resolve_inferred_array_from_type(checker, declared, inferred);
|
||||
resolved != declared {
|
||||
if !types.equal(checker.global_types[index], resolved) {
|
||||
checker.global_types[index] = resolved
|
||||
changed = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
if is_runtime_type(checker, declared) && !has_inferred_array_count(checker, declared) {
|
||||
continue
|
||||
}
|
||||
if is_runtime_type(checker, checker.global_demands[index]) {
|
||||
@@ -4935,7 +4972,7 @@ build_block :: proc(
|
||||
if statement_id != ast.INVALID_STMT && int(statement_id) < len(ctx.local_types) &&
|
||||
is_runtime_type(checker, ctx.local_types[statement_id]) &&
|
||||
(types.is_constraint(declared) || is_undefined_expr(checker, statement.expr) ||
|
||||
open_const_decl || numeric_arithmetic_decl) {
|
||||
open_const_decl || numeric_arithmetic_decl || has_inferred_array_count(checker, declared)) {
|
||||
declared = ctx.local_types[statement_id]
|
||||
}
|
||||
// A still-unresolved constraint means the initializer's numeric
|
||||
@@ -4974,7 +5011,7 @@ build_block :: proc(
|
||||
ctx.problematic^ = true
|
||||
continue
|
||||
}
|
||||
if !is_runtime_type(checker, declared) {
|
||||
if !is_runtime_type(checker, declared) || has_inferred_array_count(checker, declared) {
|
||||
id := source.addf(
|
||||
checker.diagnostics,
|
||||
statement.span,
|
||||
@@ -7477,6 +7514,10 @@ build_globals :: proc(checker: ^Checker) {
|
||||
calls: [dynamic]hir.Function_Id
|
||||
calls.allocator = checker.allocator
|
||||
declared := resolve_inferred_array(checker, type_from_syntax(checker, global.type, global.pkg, global.file), global.expr)
|
||||
if has_inferred_array_count(checker, declared) &&
|
||||
is_runtime_type(checker, checker.global_types[global_index]) {
|
||||
declared = checker.global_types[global_index]
|
||||
}
|
||||
expected := types.INVALID
|
||||
if is_runtime_type(checker, declared) {
|
||||
expected = declared
|
||||
|
||||
+5
-3
@@ -1981,12 +1981,14 @@ comptime_value_params_specialize_by_value_and_omit_runtime_args :: proc(t: ^test
|
||||
return data
|
||||
}
|
||||
main func() void {
|
||||
four [4]u8 :: make_array(4)
|
||||
eight [8]u8 :: make_array(8)
|
||||
again [4]u8 :: make_array(4)
|
||||
four [_]u8 :: make_array(4)
|
||||
eight [_]u8 :: make_array(8)
|
||||
again [_]u8 :: make_array(4)
|
||||
literal [_]u8 :: [1, 2, 3, 4]
|
||||
_ = four
|
||||
_ = eight
|
||||
_ = again
|
||||
_ = literal
|
||||
}
|
||||
`
|
||||
source_file := source.Source{path="test.bro", text=text}
|
||||
|
||||
@@ -8,7 +8,7 @@ value func($N usize) usize {
|
||||
}
|
||||
|
||||
main func() i32 {
|
||||
four [4]u8 :: make_array(4)
|
||||
four [_]u8 :: make_array(4)
|
||||
if four.len != 4 {
|
||||
return 1
|
||||
}
|
||||
@@ -16,7 +16,7 @@ main func() i32 {
|
||||
return 2
|
||||
}
|
||||
|
||||
eight [8]u8 :: make_array(8)
|
||||
eight [_]u8 :: make_array(8)
|
||||
if eight.len != 8 {
|
||||
return 3
|
||||
}
|
||||
@@ -24,7 +24,7 @@ main func() i32 {
|
||||
return 4
|
||||
}
|
||||
|
||||
again [4]u8 :: make_array(4)
|
||||
again [_]u8 :: [1, 2, 3, 4]
|
||||
if again.len != 4 {
|
||||
return 5
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user