diff --git a/bootstrap/src/compiler.rs b/bootstrap/src/compiler.rs index db441062ae..6306883e01 100644 --- a/bootstrap/src/compiler.rs +++ b/bootstrap/src/compiler.rs @@ -25528,12 +25528,15 @@ fn collect_top_level_decls(node: &Node, out: &mut Vec<(String, &'static str)>) { fn duplicate_top_level_decls(ast: &Node) -> Vec { let mut decls: Vec<(String, &'static str)> = Vec::new(); collect_top_level_decls(ast, &mut decls); + for c in ast.children.iter().filter(|c| c.kind == NodeKind::ConstDecl) { // module level only: a local `const` is scope + decls.push((c.name.clone(), if c.extra_mutable { "var" } else { "const" })); + } // Four namespaces, not two. A test name collides only with another test // name: `struct deque_clear` beside `test deque_clear` is not a conflict // in any backend, and 138 names across 54 specs are exactly that shape. // Folding tests into "type" would report every one of them. let ns = |k: &str| match k { - "fn" => "value", + "fn" | "const" | "var" => "value", "test" => "test", "bench" => "bench", _ => "type", @@ -25772,6 +25775,13 @@ drop the parameter from the declaration and keep it at each use, where it is und } for d in duplicate_top_level_decls(ast) { + if let DuplicateDecl::SameNamespace(name, kind, n) = &d { // an error, as E_NAME is in the self-hosted core (#10421); a bench stays a warning + if *kind != "bench" { + result.error_count += 1; + result.errors.push(format!("error: `{kind} {name}` is declared {n} times -- every backend rejects a redeclaration (gen-js kept the first copy, gen-c the last, before check refused it)")); + continue; + } + } let msg = match d { DuplicateDecl::SameNamespace(name, kind, n) => format!( "warning: `{kind} {name}` is declared {n} times -- every backend rejects a redeclaration" diff --git a/bootstrap/stage0/FROZEN_HASH b/bootstrap/stage0/FROZEN_HASH index 4cf98fee59..2da0514de6 100644 --- a/bootstrap/stage0/FROZEN_HASH +++ b/bootstrap/stage0/FROZEN_HASH @@ -1 +1 @@ -fad12492937aa2196d2adbf560305e367f5dd1183a3107137d5bc0c1c26033ab bootstrap/src/compiler.rs +092ec34c7ce7592ab4a5124277a8cc4c5852d358874af8fa5a96ae991f41b655 bootstrap/src/compiler.rs diff --git a/bootstrap/tests/duplicate_declarations.rs b/bootstrap/tests/duplicate_declarations.rs index a2b24cd470..1711520441 100644 --- a/bootstrap/tests/duplicate_declarations.rs +++ b/bootstrap/tests/duplicate_declarations.rs @@ -40,6 +40,7 @@ const DUP_STRUCT: &str = r#" module D1 { struct A { x : i32, } struct A { y : i32, } + const K : i32 = 1; const K : i32 = 2; fn f(a: A) -> i32 { return a.x; } } "#; @@ -47,7 +48,8 @@ module D1 { const CLEAN: &str = r#" module D3 { struct C { x : i32, } - fn g(c: C) -> i32 { return c.x; } + fn g(c: C) -> i32 { const K : i32 = 1; return c.x + K; } + fn h(c: C) -> i32 { const K : i32 = 2; return c.x + K; } } "#; @@ -55,11 +57,11 @@ module D3 { fn a_name_declared_twice_is_reported_with_its_count() { let (_, out) = check(DUP_STRUCT, "twice"); assert!( - out.contains("`struct A` is declared 2 times"), + out.contains("`struct A` is declared 2 times") && out.contains("`const K` is declared 2 times"), "the finding must name the kind, the name and the count:\n{out}" ); assert!( - out.contains("every backend rejects a redeclaration"), + out.contains("error: ") && out.contains("every backend rejects a redeclaration"), "and say what the consequence is:\n{out}" ); } @@ -119,23 +121,11 @@ module D4 { "structenum", ); assert!( - out.contains("is declared 2 times") && out.contains("every backend rejects"), + out.contains("is declared 2 times") && out.contains("error: "), "struct and enum sharing a name is a same-namespace collision:\n{out}" ); } -#[test] -fn the_finding_is_a_warning_and_does_not_change_the_exit_code() { - // Measured over the corpus before this landed: 651 specs, 573 exit 0 and - // 78 exit 1, and not one spec's code moved. Pinned here so a later change - // to an error is a deliberate, test-visible decision. - let (rc, out) = check(DUP_STRUCT, "exitcode"); - assert_eq!( - rc, 0, - "the duplicate finding is a warning; promoting it must be deliberate:\n{out}" - ); -} - // --------------------------------------------------------------------------- // A test block is a declaration too (#3479) // diff --git a/bootstrap/tests/fixtures/duplicate_toplevel/accept/sibling_blocks.t27 b/bootstrap/tests/fixtures/duplicate_toplevel/accept/sibling_blocks.t27 new file mode 100644 index 0000000000..ff83ba709a --- /dev/null +++ b/bootstrap/tests/fixtures/duplicate_toplevel/accept/sibling_blocks.t27 @@ -0,0 +1,30 @@ +// Sibling blocks reuse a name: that is scope, not a redeclaration. The same +// names are used as locals in two functions and in two tests, and a test shares +// the name of a function (separate namespaces). +module SiblingBlocks; + +pub const LIMIT : u32 = 25; + +pub fn first() -> u32 { + const limit : u32 = 1; + var acc : u32 = 0; + acc = acc + limit; + return acc; +} + +pub fn second() -> u32 { + const limit : u32 = 2; + var acc : u32 = 0; + acc = acc + limit; + return acc; +} + +test "first" { + const limit : u32 = 1; + assert first() == limit; +} + +test "second" { + const limit : u32 = 2; + assert second() == limit; +} diff --git a/bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_const.t27 b/bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_const.t27 new file mode 100644 index 0000000000..6edba30b1b --- /dev/null +++ b/bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_const.t27 @@ -0,0 +1,6 @@ +// A module const declared twice: gen-js keeps the first value (25), gen-c +// reads the last (100), and `check` used to say nothing (#10421). +module DupConst; + +pub const LIMIT : u32 = 25; +pub const LIMIT : u32 = 100; diff --git a/bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_const_and_fn.t27 b/bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_const_and_fn.t27 new file mode 100644 index 0000000000..a8a821a91b --- /dev/null +++ b/bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_const_and_fn.t27 @@ -0,0 +1,28 @@ +module DupProbe; + +pub const LIMIT : u32 = 25; +pub const LIMIT : u32 = 100; + +pub fn pick() -> u32 { + return 1; +} + +pub fn pick() -> u32 { + return 2; +} + +test "the first LIMIT" { + assert LIMIT == 25; +} + +test "the last LIMIT" { + assert LIMIT == 100; +} + +test "the first pick" { + assert pick() == 1; +} + +test "the last pick" { + assert pick() == 2; +} diff --git a/bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_fn.t27 b/bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_fn.t27 new file mode 100644 index 0000000000..ebaafc150b --- /dev/null +++ b/bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_fn.t27 @@ -0,0 +1,10 @@ +// A function declared twice. +module DupFn; + +pub fn pick() -> u32 { + return 1; +} + +pub fn pick() -> u32 { + return 2; +} diff --git a/bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_struct.t27 b/bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_struct.t27 new file mode 100644 index 0000000000..99861287c5 --- /dev/null +++ b/bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_struct.t27 @@ -0,0 +1,10 @@ +// A struct declared twice. +module DupStruct; + +pub struct Pair { + lo: u32, +} + +pub struct Pair { + hi: u32, +} diff --git a/bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_test.t27 b/bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_test.t27 new file mode 100644 index 0000000000..ee4330b822 --- /dev/null +++ b/bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_test.t27 @@ -0,0 +1,14 @@ +// A test block declared twice under one name. +module DupTest; + +pub fn one() -> u32 { + return 1; +} + +test "same name" { + assert one() == 1; +} + +test "same name" { + assert one() == 1; +} diff --git a/bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_var.t27 b/bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_var.t27 new file mode 100644 index 0000000000..baf938af39 --- /dev/null +++ b/bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_var.t27 @@ -0,0 +1,5 @@ +// A module var declared twice. +module DupVar; + +var counter : u32 = 0; +var counter : u32 = 1; diff --git a/tools/specs_generate_baseline.txt b/tools/specs_generate_baseline.txt index 72c7450d02..ba9c821a2b 100644 --- a/tools/specs_generate_baseline.txt +++ b/tools/specs_generate_baseline.txt @@ -25,3 +25,14 @@ bootstrap/tests/fixtures/core_selfhost/refuse/e16_compound_cross_sign.t27 | Erro bootstrap/tests/fixtures/core_selfhost/refuse/e16_const_too_wide.t27 | Error: typecheck refused bootstrap/tests/fixtures/core_selfhost/refuse/e16_const_too_wide.t27: 1 error(s), so nothing is generated for it (#6446) bootstrap/tests/fixtures/core_selfhost/refuse/e16_const_too_wide_signed.t27 | Error: typecheck refused bootstrap/tests/fixtures/core_selfhost/refuse/e16_const_too_wide_signed.t27: 1 error(s), so nothing is generated for it (#644 bootstrap/tests/fixtures/core_selfhost/refuse/e4_arity_return.t27 | Error: typecheck refused bootstrap/tests/fixtures/core_selfhost/refuse/e4_arity_return.t27: 1 error(s), so nothing is generated for it (#6446) +# Not a debt: #10421's refuse fixtures. A duplicated top-level name is now a typecheck error, so no backend generates them; the self-hosted core refuses the same names with E_NAME. +bootstrap/tests/fixtures/core_selfhost/refuse/e5_duplicate_const.t27 | Error: typecheck refused bootstrap/tests/fixtures/core_selfhost/refuse/e5_duplicate_const.t27: 1 error(s), so nothing is generated for it (#6446) +bootstrap/tests/fixtures/core_selfhost/refuse/e5_duplicate_fn.t27 | Error: typecheck refused bootstrap/tests/fixtures/core_selfhost/refuse/e5_duplicate_fn.t27: 1 error(s), so nothing is generated for it (#6446) +bootstrap/tests/fixtures/core_selfhost/refuse/e5_duplicate_test.t27 | Error: typecheck refused bootstrap/tests/fixtures/core_selfhost/refuse/e5_duplicate_test.t27: 1 error(s), so nothing is generated for it (#6446) +bootstrap/tests/fixtures/core_selfhost/refuse/e5_var_and_fn_one_name.t27 | Error: typecheck refused bootstrap/tests/fixtures/core_selfhost/refuse/e5_var_and_fn_one_name.t27: 1 error(s), so nothing is generated for it (#6446) +bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_const.t27 | Error: typecheck refused bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_const.t27: 1 error(s), so nothing is generated for it (#6446) +bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_const_and_fn.t27 | Error: typecheck refused bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_const_and_fn.t27: 2 error(s), so nothing is generated for it (#6446) +bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_fn.t27 | Error: typecheck refused bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_fn.t27: 1 error(s), so nothing is generated for it (#6446) +bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_struct.t27 | Error: typecheck refused bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_struct.t27: 1 error(s), so nothing is generated for it (#6446) +bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_test.t27 | Error: typecheck refused bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_test.t27: 1 error(s), so nothing is generated for it (#6446) +bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_var.t27 | Error: typecheck refused bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_var.t27: 1 error(s), so nothing is generated for it (#6446)