From: Egas Ribeiro <[email protected]>
Add diagnostics for refutable patterns appearing in positions that
require irrefutability: let bindings (without an else clause) and
function parameters. Uses the type-aware is_refutable(BaseType&)
introduced in the previous commit.
The existing syntactic check for the function parameters case done
during AST lowering is reduced to handle only bare literal patterns,
with the comprehensive check deferred to PatternChecker after the type
checker populates the necessary type information.
Add comprehensive tests that cover the implemented refutability checks.
Addresses: Rust-GCC#2082
gcc/rust/ChangeLog:
* checks/errors/rust-hir-pattern-analysis.cc
(PatternChecker::visit): Diagnose refutable patterns in Function
parameters and LetStmt bindings.
* hir/rust-ast-lower-item.cc (ASTLoweringItem::visit): Remove
pattern kind whitelist; only error on bare literal patterns at
lowering.
gcc/testsuite/ChangeLog:
* rust/compile/irrefutable-alt.rs: New test.
* rust/compile/irrefutable-basic.rs: New test.
* rust/compile/irrefutable-identifier-binding.rs: New test.
* rust/compile/irrefutable-nested.rs: New test.
* rust/compile/irrefutable-path.rs: New test.
* rust/compile/irrefutable-range.rs: New test.
* rust/compile/irrefutable-reference.rs: New test.
* rust/compile/irrefutable-slice.rs: New test.
* rust/compile/irrefutable-struct.rs: New test.
* rust/compile/irrefutable-tuple.rs: New test.
* rust/compile/refutable-alt.rs: New test.
* rust/compile/refutable-binding.rs: New test.
* rust/compile/refutable-literal-let.rs: New test.
* rust/compile/refutable-literal-parameter.rs: New test.
* rust/compile/refutable-path.rs: New test.
* rust/compile/refutable-range.rs: New test.
* rust/compile/refutable-reference.rs: New test.
* rust/compile/refutable-struct.rs: New test.
* rust/compile/refutable-tuple.rs: New test.
Co-authored-by: João Novo <[email protected]>
Signed-off-by: Egas Ribeiro <[email protected]>
---
This change was merged into the gccrs repository and is posted here for
upstream visibility and potential drive-by review, as requested by GCC
release managers.
Each commit email contains a link to its details on github from where you can
find the Pull-Request and associated discussions.
Commit on github:
https://github.com/Rust-GCC/gccrs/commit/f50d08d5f76127ee75502359c77e02af539ad0a2
The commit has NOT been mentioned in any issue.
The commit has been mentioned in the following pull-request(s):
- https://github.com/Rust-GCC/gccrs/pull/4571
.../errors/rust-hir-pattern-analysis.cc | 25 +++++++++++++++
gcc/rust/hir/rust-ast-lower-item.cc | 15 +++------
gcc/testsuite/rust/compile/irrefutable-alt.rs | 11 +++++++
.../rust/compile/irrefutable-basic.rs | 16 ++++++++++
.../compile/irrefutable-identifier-binding.rs | 8 +++++
.../rust/compile/irrefutable-nested.rs | 10 ++++++
.../rust/compile/irrefutable-path.rs | 12 +++++++
.../rust/compile/irrefutable-range.rs | 9 ++++++
.../rust/compile/irrefutable-reference.rs | 6 ++++
.../rust/compile/irrefutable-slice.rs | 14 ++++++++
.../rust/compile/irrefutable-struct.rs | 32 +++++++++++++++++++
.../rust/compile/irrefutable-tuple.rs | 9 ++++++
gcc/testsuite/rust/compile/refutable-alt.rs | 12 +++++++
.../rust/compile/refutable-binding.rs | 6 ++++
.../rust/compile/refutable-literal-let.rs | 5 +++
.../compile/refutable-literal-parameter.rs | 4 +++
gcc/testsuite/rust/compile/refutable-path.rs | 14 ++++++++
gcc/testsuite/rust/compile/refutable-range.rs | 9 ++++++
.../rust/compile/refutable-reference.rs | 6 ++++
.../rust/compile/refutable-struct.rs | 23 +++++++++++++
gcc/testsuite/rust/compile/refutable-tuple.rs | 10 ++++++
21 files changed, 245 insertions(+), 11 deletions(-)
create mode 100644 gcc/testsuite/rust/compile/irrefutable-alt.rs
create mode 100644 gcc/testsuite/rust/compile/irrefutable-basic.rs
create mode 100644 gcc/testsuite/rust/compile/irrefutable-identifier-binding.rs
create mode 100644 gcc/testsuite/rust/compile/irrefutable-nested.rs
create mode 100644 gcc/testsuite/rust/compile/irrefutable-path.rs
create mode 100644 gcc/testsuite/rust/compile/irrefutable-range.rs
create mode 100644 gcc/testsuite/rust/compile/irrefutable-reference.rs
create mode 100644 gcc/testsuite/rust/compile/irrefutable-slice.rs
create mode 100644 gcc/testsuite/rust/compile/irrefutable-struct.rs
create mode 100644 gcc/testsuite/rust/compile/irrefutable-tuple.rs
create mode 100644 gcc/testsuite/rust/compile/refutable-alt.rs
create mode 100644 gcc/testsuite/rust/compile/refutable-binding.rs
create mode 100644 gcc/testsuite/rust/compile/refutable-literal-let.rs
create mode 100644 gcc/testsuite/rust/compile/refutable-literal-parameter.rs
create mode 100644 gcc/testsuite/rust/compile/refutable-path.rs
create mode 100644 gcc/testsuite/rust/compile/refutable-range.rs
create mode 100644 gcc/testsuite/rust/compile/refutable-reference.rs
create mode 100644 gcc/testsuite/rust/compile/refutable-struct.rs
create mode 100644 gcc/testsuite/rust/compile/refutable-tuple.rs
diff --git a/gcc/rust/checks/errors/rust-hir-pattern-analysis.cc
b/gcc/rust/checks/errors/rust-hir-pattern-analysis.cc
index ed1bd49ba..81f872fdc 100644
--- a/gcc/rust/checks/errors/rust-hir-pattern-analysis.cc
+++ b/gcc/rust/checks/errors/rust-hir-pattern-analysis.cc
@@ -484,6 +484,17 @@ PatternChecker::visit (UseDeclaration &)
void
PatternChecker::visit (Function &function)
{
+ for (auto ¶m : function.get_function_params ())
+ {
+ TyTy::BaseType *param_ty;
+ bool ok
+ = tyctx.lookup_type (param.get_mappings ().get_hirid (), ¶m_ty);
+ rust_assert (ok);
+
+ if (param.get_param_name ().is_refutable (*param_ty))
+ rust_error_at (param.get_locus (), ErrorCode::E0005,
+ "refutable pattern in function parameter");
+ }
function.get_definition ().accept_vis (*this);
}
@@ -684,6 +695,20 @@ PatternChecker::visit (LetStmt &stmt)
{
if (stmt.has_init_expr ())
stmt.get_init_expr ().accept_vis (*this);
+
+ // skip let-else (allows refutable patterns)
+ if (stmt.has_else_expr ())
+ return;
+
+ TyTy::BaseType *binding_ty;
+ bool ok = tyctx.lookup_type (stmt.get_pattern ().get_mappings ().get_hirid
(),
+ &binding_ty);
+ if (!ok)
+ return; // type-check failed earlier
+
+ if (stmt.get_pattern ().is_refutable (*binding_ty))
+ rust_error_at (stmt.get_pattern ().get_locus (), ErrorCode::E0005,
+ "refutable pattern in local binding");
}
void
diff --git a/gcc/rust/hir/rust-ast-lower-item.cc
b/gcc/rust/hir/rust-ast-lower-item.cc
index 96b6aefbc..7f278902a 100644
--- a/gcc/rust/hir/rust-ast-lower-item.cc
+++ b/gcc/rust/hir/rust-ast-lower-item.cc
@@ -458,20 +458,13 @@ ASTLoweringItem::visit (AST::Function &function)
switch (param.get_pattern ().get_pattern_kind ())
{
- case AST::Pattern::Kind::Identifier:
- case AST::Pattern::Kind::Wildcard:
- case AST::Pattern::Kind::Tuple:
- case AST::Pattern::Kind::Struct:
- case AST::Pattern::Kind::TupleStruct:
- case AST::Pattern::Kind::Reference:
- case AST::Pattern::Kind::Grouped:
- case AST::Pattern::Kind::Slice:
- case AST::Pattern::Kind::Rest:
- break;
- default:
+ case AST::Pattern::Kind::Literal:
rust_error_at (param.get_locus (),
"refutable pattern in function argument");
continue;
+ default:
+ // defer checking for when we have type information after lowering
+ break;
}
auto translated_type = std::unique_ptr<HIR::Type> (
diff --git a/gcc/testsuite/rust/compile/irrefutable-alt.rs
b/gcc/testsuite/rust/compile/irrefutable-alt.rs
new file mode 100644
index 000000000..edc8a39ce
--- /dev/null
+++ b/gcc/testsuite/rust/compile/irrefutable-alt.rs
@@ -0,0 +1,11 @@
+#![feature(no_core)]
+#![no_core]
+enum E {
+ A,
+ B,
+}
+fn a((E::A | E::B): E) {} // { dg-bogus {sorry, unimplemented: alt pattern}
TODO { xfail *-*-* } }
+ // { dg-ice "" }
+fn main() {
+ let (E::A | E::B): E;
+}
diff --git a/gcc/testsuite/rust/compile/irrefutable-basic.rs
b/gcc/testsuite/rust/compile/irrefutable-basic.rs
new file mode 100644
index 000000000..f86d396d9
--- /dev/null
+++ b/gcc/testsuite/rust/compile/irrefutable-basic.rs
@@ -0,0 +1,16 @@
+#![feature(no_core)]
+#![no_core]
+fn _a(_: i32) {}
+fn _b(_x: i32) {}
+fn _d((_x, _y): (i32, i32)) {}
+fn _e((_x, .., _y): (i32, i32, i32, i32)) {}
+fn _f((..): (i32, i32)) {}
+fn _g((_x, (..)): (i32, (i32, i32))) {}
+fn main() {
+ let _ = 1;
+ let _x = 1;
+ let (_a, _b) = (1, 2);
+ let (_a, .., _b) = (1, 2, 3, 4);
+ let (..) = (1, 2);
+ let (_a, (..)) = (1, (2, 3));
+}
diff --git a/gcc/testsuite/rust/compile/irrefutable-identifier-binding.rs
b/gcc/testsuite/rust/compile/irrefutable-identifier-binding.rs
new file mode 100644
index 000000000..5cd30efb1
--- /dev/null
+++ b/gcc/testsuite/rust/compile/irrefutable-identifier-binding.rs
@@ -0,0 +1,8 @@
+#![feature(no_core)]
+#![no_core]
+fn _a(_x @ _: i32) {}
+fn _b(_x @ (_a, _b): (i32, i32)) {}
+fn main() {
+ let _x @ _ = (1, 2);
+ let _x @ (_a, _b) = (1, 2);
+}
diff --git a/gcc/testsuite/rust/compile/irrefutable-nested.rs
b/gcc/testsuite/rust/compile/irrefutable-nested.rs
new file mode 100644
index 000000000..1f3b8cb06
--- /dev/null
+++ b/gcc/testsuite/rust/compile/irrefutable-nested.rs
@@ -0,0 +1,10 @@
+#![feature(no_core)]
+#![no_core]
+fn _a(((_x, _y), _z): ((i32, i32), i32)) {}
+fn _b((_x, (_y, _z)): (i32, (i32, i32))) {}
+fn _c(&&_x: &&i32) {}
+fn main() {
+ let ((_a, _b), _c) = ((1, 2), 3);
+ let (_a, (_b, _c)) = (1, (2, 3));
+ let &&_x: &&i32;
+}
diff --git a/gcc/testsuite/rust/compile/irrefutable-path.rs
b/gcc/testsuite/rust/compile/irrefutable-path.rs
new file mode 100644
index 000000000..202673dc4
--- /dev/null
+++ b/gcc/testsuite/rust/compile/irrefutable-path.rs
@@ -0,0 +1,12 @@
+#![feature(no_core)]
+#![no_core]
+enum E {
+ A,
+}
+
+// TODO: this test ICEs because Path patterns in function parameters aren't
fully implemented.
+// once implementation is added this test should compile normally
+fn a(E::A: E) {} // { dg-ice "" }
+fn main() {
+ let E::A: E;
+}
diff --git a/gcc/testsuite/rust/compile/irrefutable-range.rs
b/gcc/testsuite/rust/compile/irrefutable-range.rs
new file mode 100644
index 000000000..89e535025
--- /dev/null
+++ b/gcc/testsuite/rust/compile/irrefutable-range.rs
@@ -0,0 +1,9 @@
+#![feature(no_core)]
+#![no_core]
+// TODO: remove xfails once parsing of 0.. in fn params and let bindings is
implemented.
+// { dg-do compile { xfail *-*-* } }
+// { dg-excess-errors "parsing of open-ended ranges in patterns not
implemented" { xfail *-*-* } }
+fn a(0..: u8) {}
+fn main() {
+ let 0..: u8;
+}
diff --git a/gcc/testsuite/rust/compile/irrefutable-reference.rs
b/gcc/testsuite/rust/compile/irrefutable-reference.rs
new file mode 100644
index 000000000..73bd14e5f
--- /dev/null
+++ b/gcc/testsuite/rust/compile/irrefutable-reference.rs
@@ -0,0 +1,6 @@
+#![feature(no_core)]
+#![no_core]
+fn _c(&_x: &i32) {}
+fn main() {
+ let &_x = &1; // { dg-bogus {sorry, unimplemented: reference pattern let}
TODO { xfail *-*-* } }
+}
diff --git a/gcc/testsuite/rust/compile/irrefutable-slice.rs
b/gcc/testsuite/rust/compile/irrefutable-slice.rs
new file mode 100644
index 000000000..810c7da52
--- /dev/null
+++ b/gcc/testsuite/rust/compile/irrefutable-slice.rs
@@ -0,0 +1,14 @@
+#![feature(no_core)]
+#![no_core]
+// TODO: this test ICEs because slices in function parameters aren't fully
implemented.
+// once implementation is added this test should compile normally
+// { dg-ice "" }
+fn a([a, b, c]: [i32; 3]) {}
+fn b([a, .., c]: [i32; 3]) {}
+fn c([a, .., c]: [i32; 5]) {}
+fn main() {
+ let [..]: [i32];
+ let [a, b, c]: [i32; 3];
+ let [a, .., c]: [i32; 3];
+ let [a, .., c]: [i32; 5];
+}
diff --git a/gcc/testsuite/rust/compile/irrefutable-struct.rs
b/gcc/testsuite/rust/compile/irrefutable-struct.rs
new file mode 100644
index 000000000..feeb1b32f
--- /dev/null
+++ b/gcc/testsuite/rust/compile/irrefutable-struct.rs
@@ -0,0 +1,32 @@
+#![feature(no_core)]
+#![no_core]
+
+// this test ICEs because in CompilePatternBindings::visit
(HIR::TupleStructPattern &pattern)
+// we are missing an implementation handling 'E::T(a, .., b)' with rest
patterns
+// once that is implemented, this test should pass
+// { dg-ice "" }
+//
+struct S {
+ x: i32,
+ y: i32,
+}
+struct T(i32, i32);
+enum E {
+ T(i32, i32),
+}
+fn a(S { x, y }: S) {}
+fn b(S { x: a, y: b }: S) {}
+fn c(S { x: _, y: _ }: S) {}
+fn d(T(a, b): T) {}
+fn e(T(a, .., b): T) {}
+fn g(E::T(a, .., b): E) {}
+fn h(E::T(a, b): E) {}
+fn main() {
+ let S { x, y }: S;
+ let S { x: a, y: b }: S;
+ let S { x: _, y: _ }: S;
+ let T(a, b): T;
+ let T(a, .., b): T;
+ let E::T(a, .., b): E;
+ let E::T(a, b): E;
+}
diff --git a/gcc/testsuite/rust/compile/irrefutable-tuple.rs
b/gcc/testsuite/rust/compile/irrefutable-tuple.rs
new file mode 100644
index 000000000..9b64e7f77
--- /dev/null
+++ b/gcc/testsuite/rust/compile/irrefutable-tuple.rs
@@ -0,0 +1,9 @@
+#![feature(no_core)]
+#![no_core]
+enum E {
+ A(i32),
+}
+fn _f((E::A(x), y): (E, i32)) {}
+fn main() {
+ let (E::A(x), y): (E, i32);
+}
diff --git a/gcc/testsuite/rust/compile/refutable-alt.rs
b/gcc/testsuite/rust/compile/refutable-alt.rs
new file mode 100644
index 000000000..18184be9a
--- /dev/null
+++ b/gcc/testsuite/rust/compile/refutable-alt.rs
@@ -0,0 +1,12 @@
+#![feature(no_core)]
+#![no_core]
+enum E {
+ A,
+ B,
+ C,
+}
+fn a((E::A | E::B): E) {} // { dg-bogus {sorry, unimplemented: alt pattern}
TODO { xfail *-*-* } }
+ // { dg-ice "" }
+fn main() {
+ let (E::A | E::B): E;
+}
diff --git a/gcc/testsuite/rust/compile/refutable-binding.rs
b/gcc/testsuite/rust/compile/refutable-binding.rs
new file mode 100644
index 000000000..f0c7366e7
--- /dev/null
+++ b/gcc/testsuite/rust/compile/refutable-binding.rs
@@ -0,0 +1,6 @@
+#![feature(no_core)]
+#![no_core]
+fn f(a @ 1: i32){} // { dg-error "refutable pattern" }
+fn main() {
+ let a @ 1: i32; // { dg-error "refutable pattern" }
+}
diff --git a/gcc/testsuite/rust/compile/refutable-literal-let.rs
b/gcc/testsuite/rust/compile/refutable-literal-let.rs
new file mode 100644
index 000000000..4aaa92e82
--- /dev/null
+++ b/gcc/testsuite/rust/compile/refutable-literal-let.rs
@@ -0,0 +1,5 @@
+#![feature(no_core)]
+#![no_core]
+fn main() {
+ let 1: i32; // { dg-error "refutable pattern in local" }
+}
diff --git a/gcc/testsuite/rust/compile/refutable-literal-parameter.rs
b/gcc/testsuite/rust/compile/refutable-literal-parameter.rs
new file mode 100644
index 000000000..cae7d8f06
--- /dev/null
+++ b/gcc/testsuite/rust/compile/refutable-literal-parameter.rs
@@ -0,0 +1,4 @@
+#![feature(no_core)]
+#![no_core]
+fn a(1: i32) {} // { dg-error "refutable pattern in function" }
+
diff --git a/gcc/testsuite/rust/compile/refutable-path.rs
b/gcc/testsuite/rust/compile/refutable-path.rs
new file mode 100644
index 000000000..89da0453a
--- /dev/null
+++ b/gcc/testsuite/rust/compile/refutable-path.rs
@@ -0,0 +1,14 @@
+#![feature(no_core)]
+#![no_core]
+// { dg-ice "" }
+enum E {
+ A,
+ B,
+}
+const A: u32 = 0;
+fn a(E::A: E) {}
+fn b(A: u32) {}
+fn main() {
+ let A: u32;
+ let E::A: E;
+}
diff --git a/gcc/testsuite/rust/compile/refutable-range.rs
b/gcc/testsuite/rust/compile/refutable-range.rs
new file mode 100644
index 000000000..8e6cd9ce8
--- /dev/null
+++ b/gcc/testsuite/rust/compile/refutable-range.rs
@@ -0,0 +1,9 @@
+// TODO: remove dg-ice once range refutability is implemented.
+// Once implemented, this should emit E0005 instead of ICEing.
+#![feature(no_core)]
+#![no_core]
+// { dg-ice "" }
+fn a(1..=5: i32) {} // { dg-bogus {sorry, unimplemented: range pattern} "catch
E0005 once implemented" { xfail *-*-* } }
+fn main() {
+ let 1..=5: i32;
+}
diff --git a/gcc/testsuite/rust/compile/refutable-reference.rs
b/gcc/testsuite/rust/compile/refutable-reference.rs
new file mode 100644
index 000000000..3357f67f1
--- /dev/null
+++ b/gcc/testsuite/rust/compile/refutable-reference.rs
@@ -0,0 +1,6 @@
+#![feature(no_core)]
+#![no_core]
+fn a(&1: &i32) {} // { dg-error "refutable pattern" }
+fn main() {
+ let &1: &i32; // { dg-error "refutable pattern" }
+}
diff --git a/gcc/testsuite/rust/compile/refutable-struct.rs
b/gcc/testsuite/rust/compile/refutable-struct.rs
new file mode 100644
index 000000000..63097582c
--- /dev/null
+++ b/gcc/testsuite/rust/compile/refutable-struct.rs
@@ -0,0 +1,23 @@
+#![feature(no_core)]
+#![no_core]
+struct S {
+ x: i32,
+ y: i32,
+}
+struct T(i32, i32);
+enum E {
+ T(i32, i32),
+ U(i32),
+}
+fn a(S { x, y: 1 }: S) {} // { dg-error "refutable pattern" }
+fn b(S { x: 1, y: _ }: S) {} // { dg-error "refutable pattern" }
+fn c(T(1, _): T) {} // { dg-error "refutable pattern" }
+fn d(T(1, ..): T) {} // { dg-error "refutable pattern" }
+fn e(E::T(_, _): E) {} // { dg-error "refutable pattern" }
+fn main() {
+ let S { x, y: 1 }: S; // { dg-error "refutable pattern" }
+ let S { x: 1, y: _ }: S; // { dg-error "refutable pattern" }
+ let T(1, _): T; // { dg-error "refutable pattern" }
+ let T(1, ..): T; // { dg-error "refutable pattern" }
+ let E::T(_, _): E; // { dg-error "refutable pattern" }
+}
diff --git a/gcc/testsuite/rust/compile/refutable-tuple.rs
b/gcc/testsuite/rust/compile/refutable-tuple.rs
new file mode 100644
index 000000000..5fbc4eba8
--- /dev/null
+++ b/gcc/testsuite/rust/compile/refutable-tuple.rs
@@ -0,0 +1,10 @@
+#![feature(no_core)]
+#![no_core]
+fn a((x, 1): (i32, i32)) {} // { dg-error "refutable pattern" }
+fn b((x, .., 1): (i32, i32, i32)) {} // { dg-error "refutable pattern" }
+fn c((1, (..)): (i32, (i32, i32))) {} // { dg-error "refutable pattern" }
+fn main() {
+ let (a, 1) = (1, 2); // { dg-error "refutable pattern" }
+ let (x, .., 1): (i32, i32, i32); // { dg-error "refutable pattern" }
+ let (1, (..)): (i32, (i32, i32)); // { dg-error "refutable pattern" }
+}
--
2.54.0