From 1a8b8dd70ad85cbaf840485dd99be09ec0dc93a3 Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Fri, 17 Oct 2025 16:49:55 +0200 Subject: [PATCH 1/2] add a test for tuple coercions --- tests/ui/tuple/coercion-never.rs | 14 ++++++++++++++ tests/ui/tuple/coercion-never.stderr | 25 +++++++++++++++++++++++++ tests/ui/tuple/coercion-slice.rs | 12 ++++++++++++ tests/ui/tuple/coercion-slice.stderr | 14 ++++++++++++++ 4 files changed, 65 insertions(+) create mode 100644 tests/ui/tuple/coercion-never.rs create mode 100644 tests/ui/tuple/coercion-never.stderr create mode 100644 tests/ui/tuple/coercion-slice.rs create mode 100644 tests/ui/tuple/coercion-slice.stderr diff --git a/tests/ui/tuple/coercion-never.rs b/tests/ui/tuple/coercion-never.rs new file mode 100644 index 0000000000000..3c536dd5232ee --- /dev/null +++ b/tests/ui/tuple/coercion-never.rs @@ -0,0 +1,14 @@ +// This test checks if tuple elements are a coercion site or not. +// Note that the code here is a degenerate case, but you can get similar effects in real code, when +// unifying match arms, for example. +// +// See also coercion-slice.rs + +fn main() { + let _: ((),) = (loop {},); + + ((),) = (loop {},); //~ error: mismatched types + + let x = (loop {},); + let _: ((),) = x; //~ error: mismatched types +} diff --git a/tests/ui/tuple/coercion-never.stderr b/tests/ui/tuple/coercion-never.stderr new file mode 100644 index 0000000000000..d7566f08808af --- /dev/null +++ b/tests/ui/tuple/coercion-never.stderr @@ -0,0 +1,25 @@ +error[E0308]: mismatched types + --> $DIR/coercion-never.rs:10:6 + | +LL | ((),) = (loop {},); + | ^^ ---------- this expression has type `(!,)` + | | + | expected `!`, found `()` + | + = note: expected type `!` + found unit type `()` + +error[E0308]: mismatched types + --> $DIR/coercion-never.rs:13:20 + | +LL | let _: ((),) = x; + | ----- ^ expected `((),)`, found `(!,)` + | | + | expected due to this + | + = note: expected tuple `((),)` + found tuple `(!,)` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/tuple/coercion-slice.rs b/tests/ui/tuple/coercion-slice.rs new file mode 100644 index 0000000000000..250265f28ff08 --- /dev/null +++ b/tests/ui/tuple/coercion-slice.rs @@ -0,0 +1,12 @@ +// This test checks if tuple elements are a coercion site or not. +// Note that the code here is a degenerate case, but you can get similar effects in real code, when +// unifying match arms, for example. +// +// See also: coercion-never.rs + +fn main() { + let _: (&[u8],) = (&[],); + + let y = (&[],); + let _: (&[u8],) = y; //~ error: mismatched types +} diff --git a/tests/ui/tuple/coercion-slice.stderr b/tests/ui/tuple/coercion-slice.stderr new file mode 100644 index 0000000000000..a8ae7db4490cb --- /dev/null +++ b/tests/ui/tuple/coercion-slice.stderr @@ -0,0 +1,14 @@ +error[E0308]: mismatched types + --> $DIR/coercion-slice.rs:11:23 + | +LL | let _: (&[u8],) = y; + | -------- ^ expected `(&[u8],)`, found `(&[_; 0],)` + | | + | expected due to this + | + = note: expected tuple `(&[u8],)` + found tuple `(&[_; 0],)` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. From 5444e03d3519df6f10e316733042cd9ebda27be8 Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Fri, 17 Oct 2025 14:30:57 +0200 Subject: [PATCH 2/2] always make tuple elements a coercion site --- compiler/rustc_hir_typeck/src/expr.rs | 29 +++++++++---------- compiler/rustc_middle/src/ty/sty.rs | 12 +++++++- tests/ui/loops/loop-break-value.stderr | 4 +-- .../diverging-tuple-parts-39485.stderr | 4 +-- tests/ui/never_type/issue-10176.rs | 4 +-- tests/ui/never_type/issue-10176.stderr | 4 +-- tests/ui/tuple/array-diagnostics.rs | 2 +- tests/ui/tuple/array-diagnostics.stderr | 2 +- tests/ui/tuple/coercion-never.rs | 6 ++-- tests/ui/tuple/coercion-never.stderr | 25 ---------------- 10 files changed, 38 insertions(+), 54 deletions(-) delete mode 100644 tests/ui/tuple/coercion-never.stderr diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 658f9857e5e10..9e8a57c815c97 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1982,27 +1982,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_expr_tuple( &self, - elts: &'tcx [hir::Expr<'tcx>], + elements: &'tcx [hir::Expr<'tcx>], expected: Expectation<'tcx>, expr: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { - let flds = expected.only_has_type(self).and_then(|ty| { - let ty = self.try_structurally_resolve_type(expr.span, ty); - match ty.kind() { - ty::Tuple(flds) => Some(&flds[..]), - _ => None, - } + let mut expectations = expected + .only_has_type(self) + .and_then(|ty| self.try_structurally_resolve_type(expr.span, ty).tuple()) + .unwrap_or_default() + .iter(); + + let elements = elements.iter().map(|e| { + let ty = expectations.next().unwrap_or_else(|| self.next_ty_var(e.span)); + self.check_expr_coercible_to_type(e, ty, None); + ty }); - let elt_ts_iter = elts.iter().enumerate().map(|(i, e)| match flds { - Some(fs) if i < fs.len() => { - let ety = fs[i]; - self.check_expr_coercible_to_type(e, ety, None); - ety - } - _ => self.check_expr_with_expectation(e, NoExpectation), - }); - let tuple = Ty::new_tup_from_iter(self.tcx, elt_ts_iter); + let tuple = Ty::new_tup_from_iter(self.tcx, elements); + if let Err(guar) = tuple.error_reported() { Ty::new_error(self.tcx, guar) } else { diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index e71bb9b6bb267..f31fecc360af3 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1591,7 +1591,8 @@ impl<'tcx> Ty<'tcx> { } } - /// Iterates over tuple fields. + /// Returns a list of tuple fields. + /// /// Panics when called on anything but a tuple. #[inline] pub fn tuple_fields(self) -> &'tcx List> { @@ -1601,6 +1602,15 @@ impl<'tcx> Ty<'tcx> { } } + /// Returns a list of tuple type arguments, or `None` if `self` isn't a tuple. + #[inline] + pub fn tuple(self) -> Option<&'tcx List>> { + match self.kind() { + Tuple(args) => Some(args), + _ => None, + } + } + /// If the type contains variants, returns the valid range of variant indices. // // FIXME: This requires the optimized MIR in the case of coroutines. diff --git a/tests/ui/loops/loop-break-value.stderr b/tests/ui/loops/loop-break-value.stderr index 3b9735510bd7e..8810d16ca5986 100644 --- a/tests/ui/loops/loop-break-value.stderr +++ b/tests/ui/loops/loop-break-value.stderr @@ -232,10 +232,10 @@ LL | break (break, break); | || | | || expected because of this `break` | |expected because of this `break` - | expected `()`, found `(!, !)` + | expected `()`, found `(_, _)` | = note: expected unit type `()` - found tuple `(!, !)` + found tuple `(_, _)` error[E0308]: mismatched types --> $DIR/loop-break-value.rs:89:15 diff --git a/tests/ui/never_type/diverging-tuple-parts-39485.stderr b/tests/ui/never_type/diverging-tuple-parts-39485.stderr index 90d0d4260d2dd..aab3bd4188ff7 100644 --- a/tests/ui/never_type/diverging-tuple-parts-39485.stderr +++ b/tests/ui/never_type/diverging-tuple-parts-39485.stderr @@ -22,10 +22,10 @@ error[E0308]: mismatched types LL | fn f() -> isize { | ----- expected `isize` because of return type LL | (return 1, return 2) - | ^^^^^^^^^^^^^^^^^^^^ expected `isize`, found `(!, !)` + | ^^^^^^^^^^^^^^^^^^^^ expected `isize`, found `(_, _)` | = note: expected type `isize` - found tuple `(!, !)` + found tuple `(_, _)` error: aborting due to 2 previous errors diff --git a/tests/ui/never_type/issue-10176.rs b/tests/ui/never_type/issue-10176.rs index 41e012d023f9c..f0a5993e998ab 100644 --- a/tests/ui/never_type/issue-10176.rs +++ b/tests/ui/never_type/issue-10176.rs @@ -2,8 +2,8 @@ fn f() -> isize { //~ NOTE expected `isize` because of return type (return 1, return 2) //~^ ERROR mismatched types //~| NOTE expected type `isize` -//~| NOTE found tuple `(!, !)` -//~| NOTE expected `isize`, found `(!, !)` +//~| NOTE found tuple `(_, _)` +//~| NOTE expected `isize`, found `(_, _)` } fn main() {} diff --git a/tests/ui/never_type/issue-10176.stderr b/tests/ui/never_type/issue-10176.stderr index cd6473e0682aa..1db3aaa66d471 100644 --- a/tests/ui/never_type/issue-10176.stderr +++ b/tests/ui/never_type/issue-10176.stderr @@ -4,10 +4,10 @@ error[E0308]: mismatched types LL | fn f() -> isize { | ----- expected `isize` because of return type LL | (return 1, return 2) - | ^^^^^^^^^^^^^^^^^^^^ expected `isize`, found `(!, !)` + | ^^^^^^^^^^^^^^^^^^^^ expected `isize`, found `(_, _)` | = note: expected type `isize` - found tuple `(!, !)` + found tuple `(_, _)` error: aborting due to 1 previous error diff --git a/tests/ui/tuple/array-diagnostics.rs b/tests/ui/tuple/array-diagnostics.rs index 1929dab073169..0a88e9cc3a51d 100644 --- a/tests/ui/tuple/array-diagnostics.rs +++ b/tests/ui/tuple/array-diagnostics.rs @@ -1,7 +1,7 @@ fn main() { let _tmp = [ ("C200B40A82", 3), - ("C200B40A83", 4) //~ ERROR: expected function, found `(&'static str, {integer})` [E0618] + ("C200B40A83", 4) //~ ERROR: expected function, found `(&str, {integer})` [E0618] ("C200B40A8537", 5), ]; } diff --git a/tests/ui/tuple/array-diagnostics.stderr b/tests/ui/tuple/array-diagnostics.stderr index 629ca2b37fa5a..b8f7840ea4b6c 100644 --- a/tests/ui/tuple/array-diagnostics.stderr +++ b/tests/ui/tuple/array-diagnostics.stderr @@ -1,4 +1,4 @@ -error[E0618]: expected function, found `(&'static str, {integer})` +error[E0618]: expected function, found `(&str, {integer})` --> $DIR/array-diagnostics.rs:4:9 | LL | ("C200B40A83", 4) diff --git a/tests/ui/tuple/coercion-never.rs b/tests/ui/tuple/coercion-never.rs index 3c536dd5232ee..3e4c4480c6f1f 100644 --- a/tests/ui/tuple/coercion-never.rs +++ b/tests/ui/tuple/coercion-never.rs @@ -3,12 +3,14 @@ // unifying match arms, for example. // // See also coercion-slice.rs +// +//@ check-pass fn main() { let _: ((),) = (loop {},); - ((),) = (loop {},); //~ error: mismatched types + ((),) = (loop {},); let x = (loop {},); - let _: ((),) = x; //~ error: mismatched types + let _: ((),) = x; } diff --git a/tests/ui/tuple/coercion-never.stderr b/tests/ui/tuple/coercion-never.stderr deleted file mode 100644 index d7566f08808af..0000000000000 --- a/tests/ui/tuple/coercion-never.stderr +++ /dev/null @@ -1,25 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/coercion-never.rs:10:6 - | -LL | ((),) = (loop {},); - | ^^ ---------- this expression has type `(!,)` - | | - | expected `!`, found `()` - | - = note: expected type `!` - found unit type `()` - -error[E0308]: mismatched types - --> $DIR/coercion-never.rs:13:20 - | -LL | let _: ((),) = x; - | ----- ^ expected `((),)`, found `(!,)` - | | - | expected due to this - | - = note: expected tuple `((),)` - found tuple `(!,)` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0308`.