Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion bootstrap/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<DuplicateDecl> {
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",
Expand Down Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion bootstrap/stage0/FROZEN_HASH
Original file line number Diff line number Diff line change
@@ -1 +1 @@
fad12492937aa2196d2adbf560305e367f5dd1183a3107137d5bc0c1c26033ab bootstrap/src/compiler.rs
092ec34c7ce7592ab4a5124277a8cc4c5852d358874af8fa5a96ae991f41b655 bootstrap/src/compiler.rs
22 changes: 6 additions & 16 deletions bootstrap/tests/duplicate_declarations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,26 +40,28 @@ 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; }
}
"#;

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; }
}
"#;

#[test]
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}"
);
}
Expand Down Expand Up @@ -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)
//
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -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;
}
10 changes: 10 additions & 0 deletions bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_fn.t27
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// A function declared twice.
module DupFn;

pub fn pick() -> u32 {
return 1;
}

pub fn pick() -> u32 {
return 2;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// A struct declared twice.
module DupStruct;

pub struct Pair {
lo: u32,
}

pub struct Pair {
hi: u32,
}
14 changes: 14 additions & 0 deletions bootstrap/tests/fixtures/duplicate_toplevel/refuse/dup_test.t27
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// A module var declared twice.
module DupVar;

var counter : u32 = 0;
var counter : u32 = 1;
11 changes: 11 additions & 0 deletions tools/specs_generate_baseline.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Loading