runtime mutable global state
This commit is contained in:
+337
-15
@@ -534,6 +534,90 @@ find_import :: proc(checker: ^Checker, file: ast.File_Id, alias: symbol.Id, mark
|
||||
return id
|
||||
}
|
||||
|
||||
declared_type_named :: proc(checker: ^Checker, pkg: ast.Package_Id, name: symbol.Id) -> bool {
|
||||
id := types.find_named(&checker.module.types, u32(pkg), u32(name))
|
||||
item, ok := types.node(&checker.module.types, id)
|
||||
return ok && item.declared
|
||||
}
|
||||
|
||||
visible_name_kind :: proc(
|
||||
checker: ^Checker,
|
||||
name: symbol.Id,
|
||||
pkg: ast.Package_Id,
|
||||
file: ast.File_Id,
|
||||
locals: []Build_Local = nil,
|
||||
labels: []symbol.Id = nil,
|
||||
yield_targets: []Yield_Target = nil,
|
||||
) -> string {
|
||||
if !symbol.is_valid(name) || name == checker.sink_symbol {
|
||||
return ""
|
||||
}
|
||||
if _, ok := current_comptime_value(checker, name); ok {
|
||||
return "comptime parameter"
|
||||
}
|
||||
if _, ok := find_build_local(locals, name); ok {
|
||||
return "local"
|
||||
}
|
||||
for label in labels {
|
||||
if label == name {
|
||||
return "label"
|
||||
}
|
||||
}
|
||||
for target in yield_targets {
|
||||
if target.label == name {
|
||||
return "label"
|
||||
}
|
||||
}
|
||||
if find_import(checker, file, name) != ast.INVALID_IMPORT {
|
||||
return "import"
|
||||
}
|
||||
if find_global(checker, name, pkg) != ast.INVALID_GLOBAL {
|
||||
return "global"
|
||||
}
|
||||
if find_template(checker, name, pkg) != ast.INVALID_FUNCTION {
|
||||
return "function"
|
||||
}
|
||||
if declared_type_named(checker, pkg, name) {
|
||||
return "type"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
add_shadow_diagnostic :: proc(
|
||||
checker: ^Checker,
|
||||
span: source.Span,
|
||||
name: symbol.Id,
|
||||
decl_kind: string,
|
||||
pkg: ast.Package_Id,
|
||||
file: ast.File_Id,
|
||||
locals: []Build_Local = nil,
|
||||
labels: []symbol.Id = nil,
|
||||
yield_targets: []Yield_Target = nil,
|
||||
) -> source.Diagnostic_Id {
|
||||
kind := visible_name_kind(checker, name, pkg, file, locals, labels, yield_targets)
|
||||
if len(kind) == 0 {
|
||||
return source.INVALID_DIAGNOSTIC
|
||||
}
|
||||
return source.addf(
|
||||
checker.diagnostics,
|
||||
span,
|
||||
"%s '%s' shadows visible %s",
|
||||
decl_kind,
|
||||
symbol_text(checker, name),
|
||||
kind,
|
||||
)
|
||||
}
|
||||
|
||||
add_label_shadow_diagnostic :: proc(ctx: ^Build_Ctx, span: source.Span, label: symbol.Id) -> source.Diagnostic_Id {
|
||||
if !symbol.is_valid(label) {
|
||||
return source.INVALID_DIAGNOSTIC
|
||||
}
|
||||
return add_shadow_diagnostic(
|
||||
ctx.checker, span, label, "label",
|
||||
ctx.pkg, ctx.file, ctx.locals^[:], ctx.loop_labels^[:], ctx.yield_targets^[:],
|
||||
)
|
||||
}
|
||||
|
||||
expr_package :: proc(checker: ^Checker, expr: ast.Expr, pkg: ast.Package_Id, file: ast.File_Id, mark_used := false) -> (ast.Package_Id, bool) {
|
||||
if !symbol.is_valid(expr.qualifier) {
|
||||
return pkg, true
|
||||
@@ -1081,6 +1165,11 @@ validate_declarations :: proc(checker: ^Checker) {
|
||||
"duplicate parameter '%s'",
|
||||
symbol_text(checker, param.name),
|
||||
)
|
||||
} else {
|
||||
_ = add_shadow_diagnostic(
|
||||
checker, param.span, param.name, "parameter",
|
||||
function.pkg, function.file,
|
||||
)
|
||||
}
|
||||
append(&locals, param.name)
|
||||
if !has_comptime && types.contains_c_struct_by_value(param_type, &checker.module.types) {
|
||||
@@ -1748,6 +1837,31 @@ infer_expr :: proc(
|
||||
}
|
||||
}
|
||||
}
|
||||
if !types.is_valid(last) && symbol.is_valid(expr.qualifier) &&
|
||||
find_import(checker, file, expr.qualifier) == ast.INVALID_IMPORT {
|
||||
if global := find_global(checker, expr.qualifier, pkg); global != ast.INVALID_GLOBAL {
|
||||
base_type := checker.global_types[global]
|
||||
item, has_item := types.container(base_type, &checker.module.types)
|
||||
field_name := symbol_text(checker, expr.name)
|
||||
if has_item && (item.kind == .Array || item.kind == .Slice) {
|
||||
if field_name == "len" {
|
||||
last = types.USIZE
|
||||
} else if field_name == "ptr" &&
|
||||
(item.kind == .Slice || types.is_pointer(base_type, &checker.module.types)) {
|
||||
last = container_pointer_type(&checker.module.types, item)
|
||||
}
|
||||
}
|
||||
if types.is_pointer(base_type, &checker.module.types) {
|
||||
base_type = types.child_type(base_type, &checker.module.types)
|
||||
}
|
||||
if !types.is_valid(last) {
|
||||
_, field, ok := find_struct_field(checker, base_type, expr.name)
|
||||
if ok {
|
||||
last = field.type
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !types.is_valid(last) {
|
||||
if !symbol.is_valid(expr.qualifier) {
|
||||
if value, ok := current_comptime_value(checker, expr.name); ok {
|
||||
@@ -2200,6 +2314,11 @@ infer_statements :: proc(
|
||||
if !rhs_is_arith {
|
||||
_ = record_demand(checker, statement.expr, locals^[local_index].type, locals^[:], local_types, pkg, file)
|
||||
}
|
||||
} else if global := find_global(checker, statement.name, pkg); global != ast.INVALID_GLOBAL {
|
||||
_ = merge_global_demand(checker, global, value_type)
|
||||
if !rhs_is_arith {
|
||||
_ = record_demand(checker, statement.expr, checker.global_types[global], locals^[:], local_types, pkg, file)
|
||||
}
|
||||
}
|
||||
}
|
||||
case .Expression:
|
||||
@@ -2462,6 +2581,11 @@ merge_global_demand :: proc(checker: ^Checker, global: ast.Global_Id, demand: ty
|
||||
if index < 0 || index >= len(checker.global_demands) {
|
||||
return false
|
||||
}
|
||||
ast_global := checker.ast_module.globals[global]
|
||||
declared := type_from_syntax(checker, ast_global.type, ast_global.pkg, ast_global.file)
|
||||
if types.is_constraint(declared) && !types.constraint_accepts(declared, demand, &checker.module.types) {
|
||||
return false
|
||||
}
|
||||
changed: bool
|
||||
if checker.global_open_const[index] || checker.global_open_float[index] {
|
||||
changed = merge_open_const_demand(
|
||||
@@ -2766,6 +2890,20 @@ infer_all :: proc(checker: ^Checker) {
|
||||
if is_runtime_type(checker, declared) && !has_inferred_array_count(checker, declared) {
|
||||
continue
|
||||
}
|
||||
if types.is_constraint(declared) {
|
||||
resolved := types.INVALID
|
||||
if is_runtime_type(checker, checker.global_demands[index]) &&
|
||||
types.constraint_accepts(declared, checker.global_demands[index], &checker.module.types) {
|
||||
resolved = checker.global_demands[index]
|
||||
} else {
|
||||
resolved = types.constraint_target(declared, inferred, &checker.module.types)
|
||||
}
|
||||
if is_runtime_type(checker, resolved) && !types.equal(checker.global_types[index], resolved) {
|
||||
checker.global_types[index] = resolved
|
||||
changed = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
if is_runtime_type(checker, checker.global_demands[index]) {
|
||||
// A backward demand is authoritative; assign directly (it may cross the
|
||||
// signed/unsigned family that widening would reject).
|
||||
@@ -4229,6 +4367,43 @@ build_expr :: proc(
|
||||
})
|
||||
}
|
||||
}
|
||||
if last == hir.INVALID_EXPR && symbol.is_valid(expr.qualifier) &&
|
||||
find_import(checker, file, expr.qualifier) == ast.INVALID_IMPORT {
|
||||
if global := find_global(checker, expr.qualifier, pkg); global != ast.INVALID_GLOBAL {
|
||||
base := build_global_reference(checker, global, expr.span, global_reads)
|
||||
base_type := checker.global_types[global]
|
||||
item, has_item := types.container(base_type, &checker.module.types)
|
||||
field_name := symbol_text(checker, expr.name)
|
||||
if has_item && (item.kind == .Array || item.kind == .Slice) {
|
||||
if field_name == "len" {
|
||||
last = add_hir_expr(checker, hir.Expr{
|
||||
kind=.Length, span=expr.span, type=types.USIZE, left=base,
|
||||
target=hir.INVALID_REF, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
} else if field_name == "ptr" &&
|
||||
(item.kind == .Slice || types.is_pointer(base_type, &checker.module.types)) {
|
||||
last = add_hir_expr(checker, hir.Expr{
|
||||
kind=.Slice_Ptr, span=expr.span,
|
||||
type=container_pointer_type(&checker.module.types, item), left=base,
|
||||
target=hir.INVALID_REF, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
} else if field_name == "ptr" && item.kind == .Array {
|
||||
id := source.add(checker.diagnostics, expr.span, "arrays do not expose '.ptr'; take their address first")
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
}
|
||||
}
|
||||
if types.is_pointer(base_type, &checker.module.types) {
|
||||
base_type = types.child_type(base_type, &checker.module.types)
|
||||
}
|
||||
index, field, found := find_struct_field(checker, base_type, expr.name)
|
||||
if last == hir.INVALID_EXPR && found {
|
||||
last = add_hir_expr(checker, hir.Expr{
|
||||
kind=.Field, span=expr.span, type=field.type, integer=i64(index), left=base,
|
||||
target=hir.INVALID_REF, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
if last == hir.INVALID_EXPR {
|
||||
if !symbol.is_valid(expr.qualifier) {
|
||||
if value, ok := current_comptime_value(checker, expr.name); ok {
|
||||
@@ -4839,6 +5014,18 @@ build_block :: proc(
|
||||
ctx.problematic^ = true
|
||||
continue
|
||||
}
|
||||
if id := add_shadow_diagnostic(
|
||||
checker, statement.span, statement.name, "local",
|
||||
ctx.pkg, ctx.file, ctx.locals^[:], ctx.loop_labels^[:], ctx.yield_targets^[:],
|
||||
); id != source.INVALID_DIAGNOSTIC {
|
||||
append(&body, hir.stmt_id(len(checker.module.statements)))
|
||||
append(&checker.module.statements, hir.Stmt{
|
||||
kind = .Trap, span = statement.span, expr = hir.INVALID_EXPR,
|
||||
local = hir.INVALID_LOCAL, diagnostic = id,
|
||||
})
|
||||
ctx.problematic^ = true
|
||||
continue
|
||||
}
|
||||
local_id := hir.local_id(len(ctx.hir_locals^))
|
||||
append(ctx.hir_locals, hir.Local{
|
||||
name = statement.name, type = value_type, mutable = !statement.immutable,
|
||||
@@ -4954,6 +5141,18 @@ build_block :: proc(
|
||||
ctx.problematic^ = true
|
||||
continue
|
||||
}
|
||||
if id := add_shadow_diagnostic(
|
||||
checker, statement.span, statement.name, "local",
|
||||
ctx.pkg, ctx.file, ctx.locals^[:], ctx.loop_labels^[:], ctx.yield_targets^[:],
|
||||
); id != source.INVALID_DIAGNOSTIC {
|
||||
append(&body, hir.stmt_id(len(checker.module.statements)))
|
||||
append(&checker.module.statements, hir.Stmt{
|
||||
kind = .Trap, span = statement.span, expr = hir.INVALID_EXPR,
|
||||
local = hir.INVALID_LOCAL, diagnostic = id,
|
||||
})
|
||||
ctx.problematic^ = true
|
||||
continue
|
||||
}
|
||||
local_id := hir.local_id(len(ctx.hir_locals^))
|
||||
append(ctx.hir_locals, hir.Local{
|
||||
name = statement.name, type = value_type, mutable = !statement.immutable,
|
||||
@@ -5094,13 +5293,46 @@ build_block :: proc(
|
||||
}
|
||||
local, found := find_build_local(ctx.locals^[:], statement.name)
|
||||
if !found {
|
||||
id := source.addf(checker.diagnostics, statement.span, "cannot assign unresolved local '%s'", symbol_text(checker, statement.name))
|
||||
global := find_global(checker, statement.name, ctx.pkg)
|
||||
if global == ast.INVALID_GLOBAL {
|
||||
id := source.addf(checker.diagnostics, statement.span, "cannot assign unresolved local '%s'", symbol_text(checker, statement.name))
|
||||
append(&body, hir.stmt_id(len(checker.module.statements)))
|
||||
append(&checker.module.statements, hir.Stmt{
|
||||
kind = .Trap, span = statement.span, expr = hir.INVALID_EXPR,
|
||||
local = hir.INVALID_LOCAL, diagnostic = id,
|
||||
})
|
||||
ctx.problematic^ = true
|
||||
continue
|
||||
}
|
||||
target_expr := build_global_reference(checker, global, statement.span, ctx.global_reads)
|
||||
target_type := checker.module.exprs[target_expr].type
|
||||
if !hir_location_writable(checker, target_expr, ctx.locals^[:]) {
|
||||
id := source.add(checker.diagnostics, statement.span, "assignment target is not writable")
|
||||
append(&body, hir.stmt_id(len(checker.module.statements)))
|
||||
append(&checker.module.statements, hir.Stmt{
|
||||
kind=.Trap, span=statement.span, local=hir.INVALID_LOCAL,
|
||||
target=hir.INVALID_EXPR, expr=hir.INVALID_EXPR, diagnostic=id,
|
||||
})
|
||||
ctx.problematic^ = true
|
||||
continue
|
||||
}
|
||||
value: hir.Expr_Id
|
||||
if statement.expr == ast.INVALID_EXPR {
|
||||
value, _ = build_value_source(ctx, &body, statement.body, target_type, statement.span, statement.label, statement.value_control_flow)
|
||||
} else {
|
||||
value = build_expr(
|
||||
checker, statement.expr, ctx.locals^[:], ctx.global_reads, ctx.calls,
|
||||
target_type, ctx.pkg, ctx.file,
|
||||
)
|
||||
value = coerce_expr(checker, value, target_type, statement.span)
|
||||
}
|
||||
append(&body, hir.stmt_id(len(checker.module.statements)))
|
||||
append(&checker.module.statements, hir.Stmt{
|
||||
kind = .Trap, span = statement.span, expr = hir.INVALID_EXPR,
|
||||
local = hir.INVALID_LOCAL, diagnostic = id,
|
||||
kind=.Assignment, span=statement.span, local=hir.INVALID_LOCAL,
|
||||
assignment_op=.Set,
|
||||
target=target_expr, expr=value, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
ctx.problematic^ = true
|
||||
ctx.problematic^ = ctx.problematic^ || checker.module.exprs[value].kind == .Invalid
|
||||
continue
|
||||
}
|
||||
if !local.mutable {
|
||||
@@ -5343,6 +5575,12 @@ build_block :: proc(
|
||||
"'if' unwrap captures must have distinct names",
|
||||
)
|
||||
valid_unwrap = false
|
||||
} else if id := add_shadow_diagnostic(
|
||||
checker, statement.span, capture, "capture",
|
||||
ctx.pkg, ctx.file, ctx.locals^[:capture_start], ctx.loop_labels^[:], ctx.yield_targets^[:],
|
||||
); id != source.INVALID_DIAGNOSTIC {
|
||||
diagnostic = id
|
||||
valid_unwrap = false
|
||||
}
|
||||
local = hir.local_id(len(ctx.hir_locals^))
|
||||
append(ctx.hir_locals, hir.Local{name=capture, type=child, mutable=false})
|
||||
@@ -5445,6 +5683,15 @@ build_block :: proc(
|
||||
condition = invalid_hir_expr(checker, statement.span, id, types.BOOL)
|
||||
ctx.problematic^ = true
|
||||
}
|
||||
if id := add_label_shadow_diagnostic(ctx, statement.span, statement.label);
|
||||
id != source.INVALID_DIAGNOSTIC {
|
||||
append(&body, hir.stmt_id(len(checker.module.statements)))
|
||||
append(&checker.module.statements, hir.Stmt{
|
||||
kind = .Trap, span = statement.span, expr = hir.INVALID_EXPR,
|
||||
local = hir.INVALID_LOCAL, diagnostic = id,
|
||||
})
|
||||
ctx.problematic^ = true
|
||||
}
|
||||
append(ctx.loop_defer_starts, len(ctx.defers^))
|
||||
append(ctx.loop_labels, statement.label)
|
||||
append(ctx.loop_is_loop, true)
|
||||
@@ -5530,6 +5777,15 @@ build_block :: proc(
|
||||
}
|
||||
|
||||
capture_start := len(ctx.locals^)
|
||||
if statement.name != checker.sink_symbol {
|
||||
if id := add_shadow_diagnostic(
|
||||
checker, statement.span, statement.name, "capture",
|
||||
ctx.pkg, ctx.file, ctx.locals^[:], ctx.loop_labels^[:], ctx.yield_targets^[:],
|
||||
); id != source.INVALID_DIAGNOSTIC {
|
||||
diagnostic = id
|
||||
valid_loop = false
|
||||
}
|
||||
}
|
||||
item_local := hir.local_id(len(ctx.hir_locals^))
|
||||
append(ctx.hir_locals, hir.Local{name=statement.name, type=capture_type, mutable=false})
|
||||
append(ctx.locals, Build_Local{name=statement.name, type=capture_type, mutable=false, id=item_local})
|
||||
@@ -5538,12 +5794,27 @@ build_block :: proc(
|
||||
if statement.index_name == statement.name {
|
||||
diagnostic = source.add(checker.diagnostics, statement.span, "for-loop captures must have distinct names")
|
||||
valid_loop = false
|
||||
} else if id := add_shadow_diagnostic(
|
||||
checker, statement.span, statement.index_name, "capture",
|
||||
ctx.pkg, ctx.file, ctx.locals^[:capture_start], ctx.loop_labels^[:], ctx.yield_targets^[:],
|
||||
); id != source.INVALID_DIAGNOSTIC {
|
||||
diagnostic = id
|
||||
valid_loop = false
|
||||
} else {
|
||||
index_local = hir.local_id(len(ctx.hir_locals^))
|
||||
append(ctx.hir_locals, hir.Local{name=statement.index_name, type=types.USIZE, mutable=false})
|
||||
append(ctx.locals, Build_Local{name=statement.index_name, type=types.USIZE, mutable=false, id=index_local})
|
||||
}
|
||||
}
|
||||
if id := add_label_shadow_diagnostic(ctx, statement.span, statement.label);
|
||||
id != source.INVALID_DIAGNOSTIC {
|
||||
append(&body, hir.stmt_id(len(checker.module.statements)))
|
||||
append(&checker.module.statements, hir.Stmt{
|
||||
kind = .Trap, span = statement.span, expr = hir.INVALID_EXPR,
|
||||
local = hir.INVALID_LOCAL, diagnostic = id,
|
||||
})
|
||||
ctx.problematic^ = true
|
||||
}
|
||||
append(ctx.loop_defer_starts, len(ctx.defers^))
|
||||
append(ctx.loop_labels, statement.label)
|
||||
append(ctx.loop_is_loop, true)
|
||||
@@ -5636,6 +5907,15 @@ build_block :: proc(
|
||||
// A labeled block statement (`blk: { … break :blk … }`): a break target
|
||||
// with an exit-label boundary, built as a HIR `.Block`. Not a loop, so
|
||||
// unlabeled `break`/`continue` and `continue :blk` skip it.
|
||||
if id := add_label_shadow_diagnostic(ctx, statement.span, statement.label);
|
||||
id != source.INVALID_DIAGNOSTIC {
|
||||
append(&body, hir.stmt_id(len(checker.module.statements)))
|
||||
append(&checker.module.statements, hir.Stmt{
|
||||
kind = .Trap, span = statement.span, expr = hir.INVALID_EXPR,
|
||||
local = hir.INVALID_LOCAL, diagnostic = id,
|
||||
})
|
||||
ctx.problematic^ = true
|
||||
}
|
||||
append(ctx.loop_defer_starts, len(ctx.defers^))
|
||||
append(ctx.loop_labels, statement.label)
|
||||
append(ctx.loop_is_loop, false)
|
||||
@@ -6061,6 +6341,11 @@ emit_value_if :: proc(
|
||||
if _, dup := find_build_local(ctx.locals^[capture_start:], capture); dup {
|
||||
source.add(checker.diagnostics, if_stmt.span, "'if' unwrap captures must have distinct names")
|
||||
ok = false
|
||||
} else if add_shadow_diagnostic(
|
||||
checker, if_stmt.span, capture, "capture",
|
||||
ctx.pkg, ctx.file, ctx.locals^[:capture_start], ctx.loop_labels^[:], ctx.yield_targets^[:],
|
||||
) != source.INVALID_DIAGNOSTIC {
|
||||
ok = false
|
||||
}
|
||||
local = hir.local_id(len(ctx.hir_locals^))
|
||||
append(ctx.hir_locals, hir.Local{name=capture, type=child, mutable=false})
|
||||
@@ -6594,10 +6879,22 @@ build_match_arm_body :: proc(
|
||||
result: [dynamic]hir.Stmt_Id
|
||||
result.allocator = checker.allocator
|
||||
capture_start := len(ctx.locals^)
|
||||
capture_ok := true
|
||||
|
||||
if len(arm.captures) > 0 && field_index >= 0 {
|
||||
capture := arm.captures[0]
|
||||
if capture != checker.sink_symbol {
|
||||
if id := add_shadow_diagnostic(
|
||||
checker, span, capture, "capture",
|
||||
ctx.pkg, ctx.file, ctx.locals^[:], ctx.loop_labels^[:], ctx.yield_targets^[:],
|
||||
); id != source.INVALID_DIAGNOSTIC {
|
||||
append(&result, hir.stmt_id(len(checker.module.statements)))
|
||||
append(&checker.module.statements, hir.Stmt{
|
||||
kind = .Trap, span = span, expr = hir.INVALID_EXPR,
|
||||
local = hir.INVALID_LOCAL, diagnostic = id,
|
||||
})
|
||||
capture_ok = false
|
||||
}
|
||||
// The payload sits at the subject's shared carrier offset. A value capture
|
||||
// loads it; a `|@cap|` capture binds a pointer to it (mutability follows the
|
||||
// subject), aliasing the original storage via the subject location.
|
||||
@@ -6626,7 +6923,7 @@ build_match_arm_body :: proc(
|
||||
}
|
||||
}
|
||||
|
||||
body_ok := true
|
||||
body_ok := capture_ok
|
||||
if !as_value {
|
||||
built := build_block(ctx, arm.body)
|
||||
for s in built {
|
||||
@@ -6634,7 +6931,7 @@ build_match_arm_body :: proc(
|
||||
}
|
||||
delete(built, checker.allocator)
|
||||
} else {
|
||||
body_ok = build_value_arm(ctx, &result, arm.body, slot, slot_type, span)
|
||||
body_ok = build_value_arm(ctx, &result, arm.body, slot, slot_type, span) && body_ok
|
||||
}
|
||||
resize(ctx.locals, capture_start)
|
||||
return result[:], body_ok
|
||||
@@ -6911,6 +7208,10 @@ build_value_labeled_block :: proc(
|
||||
slot = new_value_slot(ctx, slot_type)
|
||||
}
|
||||
}
|
||||
if id := add_label_shadow_diagnostic(ctx, span, label); id != source.INVALID_DIAGNOSTIC {
|
||||
ctx.problematic^ = true
|
||||
return invalid_hir_expr(checker, span, id), types.INVALID
|
||||
}
|
||||
append(ctx.yield_targets, Yield_Target{
|
||||
label = label, slot = slot, slot_type = slot_type, result_optional = result_optional,
|
||||
defer_floor = len(ctx.defers^),
|
||||
@@ -7438,7 +7739,18 @@ build_globals :: proc(checker: ^Checker) {
|
||||
global_type = checker.module.exprs[expr].type
|
||||
}
|
||||
diagnostic := source.INVALID_DIAGNOSTIC
|
||||
if !is_runtime_type(checker, global_type) {
|
||||
if !global.immutable && is_undefined_expr(checker, global.expr) {
|
||||
diagnostic = source.add(
|
||||
checker.diagnostics,
|
||||
global.span,
|
||||
"'undefined' is only valid as a mutable local declaration initializer",
|
||||
)
|
||||
if !is_runtime_type(checker, global_type) {
|
||||
global_type = types.I64
|
||||
}
|
||||
expr = invalid_hir_expr(checker, global.span, diagnostic, global_type)
|
||||
}
|
||||
if diagnostic == source.INVALID_DIAGNOSTIC && !is_runtime_type(checker, global_type) {
|
||||
diagnostic = source.addf(
|
||||
checker.diagnostics,
|
||||
global.span,
|
||||
@@ -7457,15 +7769,19 @@ build_globals :: proc(checker: ^Checker) {
|
||||
expr = invalid_hir_expr(checker, global.span, diagnostic)
|
||||
}
|
||||
if !global.immutable {
|
||||
diagnostic = source.add(
|
||||
checker.diagnostics,
|
||||
global.span,
|
||||
"mutable declarations are only valid inside functions",
|
||||
)
|
||||
expr = invalid_hir_expr(checker, global.span, diagnostic)
|
||||
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
|
||||
is_static = is_static && diagnostic == source.INVALID_DIAGNOSTIC && global.immutable
|
||||
_ = hir.global_id(len(checker.module.globals))
|
||||
append(
|
||||
&checker.module.globals,
|
||||
@@ -7477,7 +7793,7 @@ build_globals :: proc(checker: ^Checker) {
|
||||
static_value = static_value,
|
||||
is_static = is_static,
|
||||
external = false,
|
||||
writable = false,
|
||||
writable = !global.immutable,
|
||||
dependencies = dependencies,
|
||||
calls = calls[:],
|
||||
direct_problem = expr_problematic(checker, expr),
|
||||
@@ -7805,6 +8121,9 @@ check :: proc(
|
||||
source.addf(diagnostics, function.span, "package declaration '%s' conflicts with a global", symbol_text(&checker, function.name))
|
||||
}
|
||||
}
|
||||
if declared_type_named(&checker, function.pkg, function.name) {
|
||||
source.addf(diagnostics, function.span, "function '%s' shadows visible type", symbol_text(&checker, function.name))
|
||||
}
|
||||
}
|
||||
for global, index in ast_module.globals {
|
||||
for previous in ast_module.globals[:index] {
|
||||
@@ -7812,6 +8131,9 @@ check :: proc(
|
||||
source.addf(diagnostics, global.span, "duplicate global '%s'", symbol_text(&checker, global.name))
|
||||
}
|
||||
}
|
||||
if declared_type_named(&checker, global.pkg, global.name) {
|
||||
source.addf(diagnostics, global.span, "global '%s' shadows visible type", symbol_text(&checker, global.name))
|
||||
}
|
||||
}
|
||||
|
||||
validate_type_nodes(&checker)
|
||||
|
||||
Reference in New Issue
Block a user