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
|
- 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
|
- 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)`
|
- 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 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
|
- 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
|
- 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)))
|
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 {
|
function_index_less :: proc(left, right: Function_Index_Entry) -> bool {
|
||||||
if left.scope != right.scope {
|
if left.scope != right.scope {
|
||||||
return left.scope < right.scope
|
return left.scope < right.scope
|
||||||
@@ -2249,8 +2268,9 @@ infer_statements :: proc(
|
|||||||
value_type := types.INVALID
|
value_type := types.INVALID
|
||||||
if !is_undefined_expr(checker, statement.expr) {
|
if !is_undefined_expr(checker, statement.expr) {
|
||||||
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)
|
||||||
|
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
|
value_type = declared_local
|
||||||
} else if types.is_constraint(declared_local) {
|
} else if types.is_constraint(declared_local) {
|
||||||
// Seed the binding in-family (INVALID on mismatch, which
|
// 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
|
// Demands accumulate in global_demands so the default never blocks a later
|
||||||
// cross-family demand (e.g. integer literal -> unsigned or float).
|
// cross-family demand (e.g. integer literal -> unsigned or float).
|
||||||
for global, index in checker.ast_module.globals {
|
for global, index in checker.ast_module.globals {
|
||||||
declared := type_from_syntax(checker, global.type, global.pkg, global.file)
|
declared := resolve_inferred_array(
|
||||||
if is_runtime_type(checker, declared) {
|
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
|
checker.global_types[index] = declared
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -2871,7 +2895,20 @@ infer_all :: proc(checker: ^Checker) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
inferred := infer_expr(checker, global.expr, nil, global.pkg, global.file)
|
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
|
continue
|
||||||
}
|
}
|
||||||
if is_runtime_type(checker, checker.global_demands[index]) {
|
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) &&
|
if statement_id != ast.INVALID_STMT && int(statement_id) < len(ctx.local_types) &&
|
||||||
is_runtime_type(checker, ctx.local_types[statement_id]) &&
|
is_runtime_type(checker, ctx.local_types[statement_id]) &&
|
||||||
(types.is_constraint(declared) || is_undefined_expr(checker, statement.expr) ||
|
(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]
|
declared = ctx.local_types[statement_id]
|
||||||
}
|
}
|
||||||
// A still-unresolved constraint means the initializer's numeric
|
// A still-unresolved constraint means the initializer's numeric
|
||||||
@@ -4974,7 +5011,7 @@ build_block :: proc(
|
|||||||
ctx.problematic^ = true
|
ctx.problematic^ = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if !is_runtime_type(checker, declared) {
|
if !is_runtime_type(checker, declared) || has_inferred_array_count(checker, declared) {
|
||||||
id := source.addf(
|
id := source.addf(
|
||||||
checker.diagnostics,
|
checker.diagnostics,
|
||||||
statement.span,
|
statement.span,
|
||||||
@@ -7477,6 +7514,10 @@ build_globals :: proc(checker: ^Checker) {
|
|||||||
calls: [dynamic]hir.Function_Id
|
calls: [dynamic]hir.Function_Id
|
||||||
calls.allocator = checker.allocator
|
calls.allocator = checker.allocator
|
||||||
declared := resolve_inferred_array(checker, type_from_syntax(checker, global.type, global.pkg, global.file), global.expr)
|
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
|
expected := types.INVALID
|
||||||
if is_runtime_type(checker, declared) {
|
if is_runtime_type(checker, declared) {
|
||||||
expected = declared
|
expected = declared
|
||||||
|
|||||||
+5
-3
@@ -1981,12 +1981,14 @@ comptime_value_params_specialize_by_value_and_omit_runtime_args :: proc(t: ^test
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
main func() void {
|
main func() void {
|
||||||
four [4]u8 :: make_array(4)
|
four [_]u8 :: make_array(4)
|
||||||
eight [8]u8 :: make_array(8)
|
eight [_]u8 :: make_array(8)
|
||||||
again [4]u8 :: make_array(4)
|
again [_]u8 :: make_array(4)
|
||||||
|
literal [_]u8 :: [1, 2, 3, 4]
|
||||||
_ = four
|
_ = four
|
||||||
_ = eight
|
_ = eight
|
||||||
_ = again
|
_ = again
|
||||||
|
_ = literal
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
source_file := source.Source{path="test.bro", text=text}
|
source_file := source.Source{path="test.bro", text=text}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ value func($N usize) usize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main func() i32 {
|
main func() i32 {
|
||||||
four [4]u8 :: make_array(4)
|
four [_]u8 :: make_array(4)
|
||||||
if four.len != 4 {
|
if four.len != 4 {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,7 @@ main func() i32 {
|
|||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
|
|
||||||
eight [8]u8 :: make_array(8)
|
eight [_]u8 :: make_array(8)
|
||||||
if eight.len != 8 {
|
if eight.len != 8 {
|
||||||
return 3
|
return 3
|
||||||
}
|
}
|
||||||
@@ -24,7 +24,7 @@ main func() i32 {
|
|||||||
return 4
|
return 4
|
||||||
}
|
}
|
||||||
|
|
||||||
again [4]u8 :: make_array(4)
|
again [_]u8 :: [1, 2, 3, 4]
|
||||||
if again.len != 4 {
|
if again.len != 4 {
|
||||||
return 5
|
return 5
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user