about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir/src/lang_items.rs2
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs11
-rw-r--r--compiler/rustc_middle/src/traits/select.rs3
-rw-r--r--compiler/rustc_middle/src/traits/structural_impls.rs2
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs44
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs9
-rw-r--r--compiler/rustc_ty_utils/src/instance.rs3
-rw-r--r--library/core/src/marker.rs9
-rw-r--r--src/test/ui/explore-issue-38412.stderr6
-rw-r--r--src/test/ui/tuple/builtin-fail.rs19
-rw-r--r--src/test/ui/tuple/builtin-fail.stderr55
-rw-r--r--src/test/ui/tuple/builtin.rs20
15 files changed, 181 insertions, 11 deletions
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index ea17c1de9b7..ca615a4912a 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -289,6 +289,8 @@ language_item_table! {
 
     Try,                     sym::Try,                 try_trait,                  Target::Trait,          GenericRequirement::None;
 
+    Tuple,                   sym::tuple_trait,         tuple_trait,                Target::Trait,          GenericRequirement::Exact(0);
+
     SliceLen,                sym::slice_len_fn,        slice_len_fn,               Target::Method(MethodKind::Inherent), GenericRequirement::None;
 
     // Language items from AST lowering
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index 755d9f8f696..c0bf04e9e59 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -651,6 +651,10 @@ pub enum ImplSource<'tcx, N> {
 
     /// ImplSource for a `const Drop` implementation.
     ConstDestruct(ImplSourceConstDestructData<N>),
+
+    /// ImplSource for a `std::marker::Tuple` implementation.
+    /// This has no nested predicates ever, so no data.
+    Tuple,
 }
 
 impl<'tcx, N> ImplSource<'tcx, N> {
@@ -665,7 +669,8 @@ impl<'tcx, N> ImplSource<'tcx, N> {
             ImplSource::Object(d) => d.nested,
             ImplSource::FnPointer(d) => d.nested,
             ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData)
-            | ImplSource::Pointee(ImplSourcePointeeData) => Vec::new(),
+            | ImplSource::Pointee(ImplSourcePointeeData)
+            | ImplSource::Tuple => Vec::new(),
             ImplSource::TraitAlias(d) => d.nested,
             ImplSource::TraitUpcasting(d) => d.nested,
             ImplSource::ConstDestruct(i) => i.nested,
@@ -683,7 +688,8 @@ impl<'tcx, N> ImplSource<'tcx, N> {
             ImplSource::Object(d) => &d.nested,
             ImplSource::FnPointer(d) => &d.nested,
             ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData)
-            | ImplSource::Pointee(ImplSourcePointeeData) => &[],
+            | ImplSource::Pointee(ImplSourcePointeeData)
+            | ImplSource::Tuple => &[],
             ImplSource::TraitAlias(d) => &d.nested,
             ImplSource::TraitUpcasting(d) => &d.nested,
             ImplSource::ConstDestruct(i) => &i.nested,
@@ -750,6 +756,7 @@ impl<'tcx, N> ImplSource<'tcx, N> {
                     nested: i.nested.into_iter().map(f).collect(),
                 })
             }
+            ImplSource::Tuple => ImplSource::Tuple,
         }
     }
 }
diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs
index e836ba47eed..53af3e90534 100644
--- a/compiler/rustc_middle/src/traits/select.rs
+++ b/compiler/rustc_middle/src/traits/select.rs
@@ -160,6 +160,9 @@ pub enum SelectionCandidate<'tcx> {
 
     /// Implementation of `const Destruct`, optionally from a custom `impl const Drop`.
     ConstDestructCandidate(Option<DefId>),
+
+    /// Witnesses the fact that a type is a tuple.
+    TupleCandidate,
 }
 
 /// The result of trait evaluation. The order is important
diff --git a/compiler/rustc_middle/src/traits/structural_impls.rs b/compiler/rustc_middle/src/traits/structural_impls.rs
index 7fbd57ac735..c526344e1f2 100644
--- a/compiler/rustc_middle/src/traits/structural_impls.rs
+++ b/compiler/rustc_middle/src/traits/structural_impls.rs
@@ -34,6 +34,8 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> {
             super::ImplSource::TraitUpcasting(ref d) => write!(f, "{:?}", d),
 
             super::ImplSource::ConstDestruct(ref d) => write!(f, "{:?}", d),
+
+            super::ImplSource::Tuple => write!(f, "ImplSource::Tuple"),
         }
     }
 }
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 5bddcd34819..871bc5c1cdb 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1497,6 +1497,7 @@ symbols! {
         tuple,
         tuple_from_req,
         tuple_indexing,
+        tuple_trait,
         two_phase,
         ty,
         type_alias_enum_variants,
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 8a65262a007..95a34d6f51f 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -1751,7 +1751,8 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
             super::ImplSource::AutoImpl(..)
             | super::ImplSource::Builtin(..)
             | super::ImplSource::TraitUpcasting(_)
-            | super::ImplSource::ConstDestruct(_) => {
+            | super::ImplSource::ConstDestruct(_)
+            | super::ImplSource::Tuple => {
                 // These traits have no associated types.
                 selcx.tcx().sess.delay_span_bug(
                     obligation.cause.span,
@@ -1829,7 +1830,8 @@ fn confirm_select_candidate<'cx, 'tcx>(
         | super::ImplSource::Builtin(..)
         | super::ImplSource::TraitUpcasting(_)
         | super::ImplSource::TraitAlias(..)
-        | super::ImplSource::ConstDestruct(_) => {
+        | super::ImplSource::ConstDestruct(_)
+        | super::ImplSource::Tuple => {
             // we don't create Select candidates with this kind of resolution
             span_bug!(
                 obligation.cause.span,
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index e84c462ca81..83bde1c50e2 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -309,6 +309,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 // User-defined transmutability impls are permitted.
                 self.assemble_candidates_from_impls(obligation, &mut candidates);
                 self.assemble_candidates_for_transmutability(obligation, &mut candidates);
+            } else if lang_items.tuple_trait() == Some(def_id) {
+                self.assemble_candidate_for_tuple(obligation, &mut candidates);
             } else {
                 if lang_items.clone_trait() == Some(def_id) {
                     // Same builtin conditions as `Copy`, i.e., every type which has builtin support
@@ -1009,4 +1011,46 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             }
         }
     }
+
+    fn assemble_candidate_for_tuple(
+        &mut self,
+        obligation: &TraitObligation<'tcx>,
+        candidates: &mut SelectionCandidateSet<'tcx>,
+    ) {
+        let self_ty = self.infcx().shallow_resolve(obligation.self_ty().skip_binder());
+        match self_ty.kind() {
+            ty::Tuple(_) => {
+                candidates.vec.push(TupleCandidate);
+            }
+            ty::Infer(ty::TyVar(_)) => {
+                candidates.ambiguous = true;
+            }
+            ty::Bool
+            | ty::Char
+            | ty::Int(_)
+            | ty::Uint(_)
+            | ty::Float(_)
+            | ty::Adt(_, _)
+            | ty::Foreign(_)
+            | ty::Str
+            | ty::Array(_, _)
+            | ty::Slice(_)
+            | ty::RawPtr(_)
+            | ty::Ref(_, _, _)
+            | ty::FnDef(_, _)
+            | ty::FnPtr(_)
+            | ty::Dynamic(_, _)
+            | ty::Closure(_, _)
+            | ty::Generator(_, _, _)
+            | ty::GeneratorWitness(_)
+            | ty::Never
+            | ty::Projection(_)
+            | ty::Opaque(_, _)
+            | ty::Param(_)
+            | ty::Bound(_, _)
+            | ty::Error(_)
+            | ty::Infer(_)
+            | ty::Placeholder(_) => {}
+        }
+    }
 }
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index e44f6665795..046a587205b 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -126,6 +126,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 let data = self.confirm_const_destruct_candidate(obligation, def_id)?;
                 ImplSource::ConstDestruct(data)
             }
+
+            TupleCandidate => ImplSource::Tuple,
         };
 
         if !obligation.predicate.is_const_if_const() {
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 3ab9b7f4083..8b15e10ba9c 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -1609,7 +1609,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         };
 
         // (*) Prefer `BuiltinCandidate { has_nested: false }`, `PointeeCandidate`,
-        // `DiscriminantKindCandidate`, and `ConstDestructCandidate` to anything else.
+        // `DiscriminantKindCandidate`, `ConstDestructCandidate`, and `TupleCandidate`
+        // to anything else.
         //
         // This is a fix for #53123 and prevents winnowing from accidentally extending the
         // lifetime of a variable.
@@ -1629,7 +1630,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 BuiltinCandidate { has_nested: false }
                 | DiscriminantKindCandidate
                 | PointeeCandidate
-                | ConstDestructCandidate(_),
+                | ConstDestructCandidate(_)
+                | TupleCandidate,
                 _,
             ) => true,
             (
@@ -1637,7 +1639,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 BuiltinCandidate { has_nested: false }
                 | DiscriminantKindCandidate
                 | PointeeCandidate
-                | ConstDestructCandidate(_),
+                | ConstDestructCandidate(_)
+                | TupleCandidate,
             ) => false,
 
             (ParamCandidate(other), ParamCandidate(victim)) => {
diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs
index edf47403c0d..05738b6c48a 100644
--- a/compiler/rustc_ty_utils/src/instance.rs
+++ b/compiler/rustc_ty_utils/src/instance.rs
@@ -291,7 +291,8 @@ fn resolve_associated_item<'tcx>(
         | traits::ImplSource::DiscriminantKind(..)
         | traits::ImplSource::Pointee(..)
         | traits::ImplSource::TraitUpcasting(_)
-        | traits::ImplSource::ConstDestruct(_) => None,
+        | traits::ImplSource::ConstDestruct(_)
+        | traits::ImplSource::Tuple => None,
     })
 }
 
diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs
index ab673c623b5..b8239ed88ac 100644
--- a/library/core/src/marker.rs
+++ b/library/core/src/marker.rs
@@ -800,6 +800,15 @@ impl<T: ?Sized> Unpin for *mut T {}
 #[rustc_on_unimplemented(message = "can't drop `{Self}`", append_const_msg)]
 pub trait Destruct {}
 
+/// A marker for tuple types.
+///
+/// The implementation of this trait is built-in and cannot be implemented
+/// for any user type.
+#[unstable(feature = "tuple_trait", issue = "none")]
+#[cfg_attr(not(bootstrap), lang = "tuple_trait")]
+#[rustc_on_unimplemented(message = "`{Self}` is not a tuple")]
+pub trait Tuple {}
+
 /// Implementations of `Copy` for primitive types.
 ///
 /// Implementations that cannot be described in Rust
diff --git a/src/test/ui/explore-issue-38412.stderr b/src/test/ui/explore-issue-38412.stderr
index e3f82137ab3..08dadb4db85 100644
--- a/src/test/ui/explore-issue-38412.stderr
+++ b/src/test/ui/explore-issue-38412.stderr
@@ -43,19 +43,19 @@ LL |     t.2;
    = note: see issue #38412 <https://github.com/rust-lang/rust/issues/38412> for more information
    = help: add `#![feature(unstable_undeclared)]` to the crate attributes to enable
 
-error[E0616]: field `3` of struct `Tuple` is private
+error[E0616]: field `3` of struct `pub_and_stability::Tuple` is private
   --> $DIR/explore-issue-38412.rs:36:7
    |
 LL |     t.3;
    |       ^ private field
 
-error[E0616]: field `4` of struct `Tuple` is private
+error[E0616]: field `4` of struct `pub_and_stability::Tuple` is private
   --> $DIR/explore-issue-38412.rs:37:7
    |
 LL |     t.4;
    |       ^ private field
 
-error[E0616]: field `5` of struct `Tuple` is private
+error[E0616]: field `5` of struct `pub_and_stability::Tuple` is private
   --> $DIR/explore-issue-38412.rs:38:7
    |
 LL |     t.5;
diff --git a/src/test/ui/tuple/builtin-fail.rs b/src/test/ui/tuple/builtin-fail.rs
new file mode 100644
index 00000000000..31208096151
--- /dev/null
+++ b/src/test/ui/tuple/builtin-fail.rs
@@ -0,0 +1,19 @@
+#![feature(tuple_trait)]
+
+fn assert_is_tuple<T: std::marker::Tuple + ?Sized>() {}
+
+struct TupleStruct(i32, i32);
+
+fn from_param_env<T>() {
+    assert_is_tuple::<T>();
+    //~^ ERROR `T` is not a tuple
+}
+
+fn main() {
+    assert_is_tuple::<i32>();
+    //~^ ERROR `i32` is not a tuple
+    assert_is_tuple::<(i32)>();
+    //~^ ERROR `i32` is not a tuple
+    assert_is_tuple::<TupleStruct>();
+    //~^ ERROR `TupleStruct` is not a tuple
+}
diff --git a/src/test/ui/tuple/builtin-fail.stderr b/src/test/ui/tuple/builtin-fail.stderr
new file mode 100644
index 00000000000..e3e29a73fdc
--- /dev/null
+++ b/src/test/ui/tuple/builtin-fail.stderr
@@ -0,0 +1,55 @@
+error[E0277]: `T` is not a tuple
+  --> $DIR/builtin-fail.rs:8:23
+   |
+LL |     assert_is_tuple::<T>();
+   |                       ^ the trait `Tuple` is not implemented for `T`
+   |
+note: required by a bound in `assert_is_tuple`
+  --> $DIR/builtin-fail.rs:3:23
+   |
+LL | fn assert_is_tuple<T: std::marker::Tuple + ?Sized>() {}
+   |                       ^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_tuple`
+help: consider restricting type parameter `T`
+   |
+LL | fn from_param_env<T: std::marker::Tuple>() {
+   |                    ++++++++++++++++++++
+
+error[E0277]: `i32` is not a tuple
+  --> $DIR/builtin-fail.rs:13:23
+   |
+LL |     assert_is_tuple::<i32>();
+   |                       ^^^ the trait `Tuple` is not implemented for `i32`
+   |
+note: required by a bound in `assert_is_tuple`
+  --> $DIR/builtin-fail.rs:3:23
+   |
+LL | fn assert_is_tuple<T: std::marker::Tuple + ?Sized>() {}
+   |                       ^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_tuple`
+
+error[E0277]: `i32` is not a tuple
+  --> $DIR/builtin-fail.rs:15:24
+   |
+LL |     assert_is_tuple::<(i32)>();
+   |                        ^^^ the trait `Tuple` is not implemented for `i32`
+   |
+note: required by a bound in `assert_is_tuple`
+  --> $DIR/builtin-fail.rs:3:23
+   |
+LL | fn assert_is_tuple<T: std::marker::Tuple + ?Sized>() {}
+   |                       ^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_tuple`
+
+error[E0277]: `TupleStruct` is not a tuple
+  --> $DIR/builtin-fail.rs:17:23
+   |
+LL |     assert_is_tuple::<TupleStruct>();
+   |                       ^^^^^^^^^^^ the trait `Tuple` is not implemented for `TupleStruct`
+   |
+note: required by a bound in `assert_is_tuple`
+  --> $DIR/builtin-fail.rs:3:23
+   |
+LL | fn assert_is_tuple<T: std::marker::Tuple + ?Sized>() {}
+   |                       ^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_tuple`
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/tuple/builtin.rs b/src/test/ui/tuple/builtin.rs
new file mode 100644
index 00000000000..d87ce526357
--- /dev/null
+++ b/src/test/ui/tuple/builtin.rs
@@ -0,0 +1,20 @@
+// check-pass
+
+#![feature(tuple_trait)]
+
+fn assert_is_tuple<T: std::marker::Tuple + ?Sized>() {}
+
+struct Unsized([u8]);
+
+fn from_param_env<T: std::marker::Tuple + ?Sized>() {
+    assert_is_tuple::<T>();
+}
+
+fn main() {
+    assert_is_tuple::<()>();
+    assert_is_tuple::<(i32,)>();
+    assert_is_tuple::<(Unsized,)>();
+    from_param_env::<()>();
+    from_param_env::<(i32,)>();
+    from_param_env::<(Unsized,)>();
+}