mutable decl syntax change

This commit is contained in:
2026-08-05 21:19:41 +02:00
parent 88b94197c9
commit 081a3fd5df
74 changed files with 870 additions and 733 deletions
+5 -17
View File
@@ -5952,7 +5952,7 @@ infer_statements :: proc(
#partial switch statement.kind {
case .Declaration:
if statement.expr == ast.INVALID_EXPR {
// Value block (`x :: { ... yield v }` / `x T = { ... }`): register the
// Value block (`x :: { ... yield v }` / `x T := { ... }`): register the
// binding (its declared type when annotated, else left open) and walk
// the block body. The build pass resolves the yielded value's type
// independently value blocks don't join the demand fixpoint.
@@ -6026,7 +6026,7 @@ infer_statements :: proc(
continue
}
expected_assignment := types.INVALID
if statement.target != ast.INVALID_EXPR {
if statement.target != ast.INVALID_EXPR && !symbol.is_valid(statement.name) {
expected_assignment = infer_expr(checker, statement.target, locals^[:], pkg, file, demanded, local_types)
target_expr := checker.ast_module.exprs[statement.target]
if target_expr.kind == .Name && !symbol.is_valid(target_expr.qualifier) {
@@ -6055,7 +6055,7 @@ infer_statements :: proc(
// type onto open-constant operands and poison their family. Operands of an
// arithmetic RHS resolve from their own authoritative uses.
rhs_is_arith := is_arith_kind(checker.ast_module.exprs[statement.expr].kind)
if statement.target != ast.INVALID_EXPR {
if statement.target != ast.INVALID_EXPR && !symbol.is_valid(statement.name) {
target_type := expected_assignment
target_expr := checker.ast_module.exprs[statement.target]
if target_expr.kind == .Name && !symbol.is_valid(target_expr.qualifier) {
@@ -10922,7 +10922,7 @@ build_block :: proc(
}
switch statement.kind {
case .Declaration:
// A value block (`x :: { ... yield v }` / `x T = { ... }`): the parser
// A value block (`x :: { ... yield v }` / `x T := { ... }`): the parser
// leaves `expr` invalid and stashes the block in `body`. Build it, then
// declare the local from the yielded value (its type for an untyped `::`).
if statement.expr == ast.INVALID_EXPR {
@@ -11123,7 +11123,7 @@ build_block :: proc(
ctx.problematic^ = ctx.problematic^ || checker.module.exprs[value].kind == .Invalid
}
case .Assignment:
if statement.target != ast.INVALID_EXPR {
if statement.target != ast.INVALID_EXPR && !symbol.is_valid(statement.name) {
target_expr := build_expr(
checker, statement.target, ctx.locals^[:], ctx.global_reads, ctx.calls,
types.INVALID, ctx.pkg, ctx.file,
@@ -14349,18 +14349,6 @@ build_globals :: proc(checker: ^Checker) {
)
expr = invalid_hir_expr(checker, global.span, diagnostic)
}
if !global.immutable {
if !types.is_valid(global.type) {
diagnostic = source.addf(
checker.diagnostics,
global.span,
"mutable global '%s' requires a type annotation",
symbol_text(checker, global.name),
)
global_type = types.I64
expr = invalid_hir_expr(checker, global.span, diagnostic, global_type)
}
}
static_value, is_static := static_integer_value(&checker.module, expr)
is_static = is_static && diagnostic == source.INVALID_DIAGNOSTIC && global.immutable
_ = hir.global_id(len(checker.module.globals))
+10 -3
View File
@@ -133,9 +133,16 @@ lex :: proc(
case ':':
start := cursor
cursor += 1
if cursor < len(bytes) && bytes[cursor] == ':' {
cursor += 1
append_token(&stream, source_file, .Colon_Colon, start, cursor)
if cursor < len(bytes) {
if bytes[cursor] == ':' {
cursor += 1
append_token(&stream, source_file, .Colon_Colon, start, cursor)
} else if bytes[cursor] == '=' {
cursor += 1
append_token(&stream, source_file, .Colon_Equal, start, cursor)
} else {
append_token(&stream, source_file, .Colon, start, cursor)
}
} else {
append_token(&stream, source_file, .Colon, start, cursor)
}
+45 -8
View File
@@ -1868,15 +1868,35 @@ parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id {
had_type = true
}
operator := current(parser)
if operator.kind == .Colon_Colon || operator.kind == .Equal {
if had_type && operator.kind == .Equal {
diagnostic := source.add(
parser.diagnostics,
operator.span,
"mutable declarations use ':=' instead of '='",
)
for current(parser).kind != .Newline &&
current(parser).kind != .Right_Brace &&
current(parser).kind != .Eof {
advance(parser)
}
id := ast.stmt_id(len(parser.module.statements))
append(&parser.module.statements, ast.Stmt{
kind=.Invalid,
span=span_from(name.span, operator.span),
expr=ast.INVALID_EXPR,
diagnostic=diagnostic,
})
return id
}
is_sink := name.kind == .Underscore && operator.kind == .Equal
if operator.kind == .Colon_Colon || operator.kind == .Colon_Equal || is_sink {
advance(parser)
skip_newlines(parser)
kind := ast.Stmt_Kind.Assignment
immutable := false
if operator.kind == .Colon_Colon || had_type {
kind = .Declaration
immutable = operator.kind == .Colon_Colon
kind := ast.Stmt_Kind.Declaration
if is_sink {
kind = .Assignment
}
immutable := operator.kind == .Colon_Colon
// A labeled value block (`x :: blk: { … yield :blk v }`): the label lets a
// `yield :blk` exit the block past a nested `if`. Block-init body + label.
if current(parser).kind == .Identifier && peek(parser).kind == .Colon {
@@ -1954,6 +1974,11 @@ parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id {
}
expr := parse_expression(parser)
assignment_name := symbol.INVALID
target_expr := parser.module.exprs[expr]
if target_expr.kind == .Name && !symbol.is_valid(target_expr.qualifier) {
assignment_name = target_expr.name
}
if _, ok := allow(parser, .Equal); ok {
skip_newlines(parser)
// A labeled value block assigned to a complex target (`a[i] = blk: { … }`).
@@ -1964,6 +1989,7 @@ parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id {
append(&parser.module.statements, ast.Stmt{
kind=.Assignment,
span=span_from(parser.module.exprs[expr].span, previous(parser).span),
name=assignment_name,
target=expr,
label=label,
expr=ast.INVALID_EXPR,
@@ -1980,6 +2006,7 @@ parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id {
append(&parser.module.statements, ast.Stmt{
kind=.Assignment,
span=span_from(parser.module.exprs[expr].span, brace.span),
name=assignment_name,
target=expr,
expr=ast.INVALID_EXPR,
body=body,
@@ -1995,6 +2022,7 @@ parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id {
append(&parser.module.statements, ast.Stmt{
kind=.Assignment,
span=span_from(parser.module.exprs[expr].span, previous(parser).span),
name=assignment_name,
target=expr,
value_control_flow=true,
expr=ast.INVALID_EXPR,
@@ -2008,6 +2036,7 @@ parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id {
append(&parser.module.statements, ast.Stmt{
kind=.Assignment,
span=span_from(parser.module.exprs[expr].span, parser.module.exprs[value].span),
name=assignment_name,
target=expr,
expr=value,
diagnostic=source.INVALID_DIAGNOSTIC,
@@ -3312,8 +3341,16 @@ parse_top_level :: proc(parser: ^Parser) {
type_syntax = parse_type(parser)
}
operator := current(parser)
if operator.kind != .Colon_Colon && operator.kind != .Equal {
source.add(parser.diagnostics, operator.span, "expected '::' or '=' after top-level name")
if operator.kind == .Equal {
source.add(parser.diagnostics, operator.span, "mutable declarations use ':=' instead of '='")
for current(parser).kind != .Newline && current(parser).kind != .Eof {
advance(parser)
}
_ = finish_statement(parser)
return
}
if operator.kind != .Colon_Colon && operator.kind != .Colon_Equal {
source.add(parser.diagnostics, operator.span, "expected '::' or ':=' after top-level name")
_ = finish_statement(parser)
return
}
+1 -1
View File
@@ -180,7 +180,7 @@ append_runner :: proc(
fmt.sbprintf(&builder, "%s.%s() catch |_| ", alias, symbol.resolve(symbols, test.name))
strings.write_string(&builder, "{\n\t\treturn .expectation_failed\n\t}\n}\n\n")
}
strings.write_string(&builder, "main func() i32 {\n\tfailed i32 = 0\n")
strings.write_string(&builder, "main func() i32 {\n\tfailed i32 := 0\n")
for entry, index in tests {
test_id := entry.function
test := module.functions[test_id]
+1
View File
@@ -16,6 +16,7 @@ Kind :: enum u8 {
Underscore,
Colon,
Colon_Colon,
Colon_Equal,
Equal,
Equal_Equal,
Bang,