diff --git a/LANGUAGE.md b/LANGUAGE.md index 6f3095a..3438e5b 100644 --- a/LANGUAGE.md +++ b/LANGUAGE.md @@ -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 diff --git a/compiler/checker/checker.odin b/compiler/checker/checker.odin index 144c07b..59130b6 100644 --- a/compiler/checker/checker.odin +++ b/compiler/checker/checker.odin @@ -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 diff --git a/compiler_tests.odin b/compiler_tests.odin index d16086a..6884381 100644 --- a/compiler_tests.odin +++ b/compiler_tests.odin @@ -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} diff --git a/examples/programs/comptime_value_params/main.bro b/examples/programs/comptime_value_params/main.bro index a72421f..d9f133b 100644 --- a/examples/programs/comptime_value_params/main.bro +++ b/examples/programs/comptime_value_params/main.bro @@ -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 }