diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index ea17c1de9b700..ca615a4912a19 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 755d9f8f69667..c0bf04e9e59d4 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), + + /// 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 e836ba47eed7a..53af3e9053419 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), + + /// 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 7fbd57ac7354a..c526344e1f263 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 5bddcd3481974..871bc5c1cdb06 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 8a65262a00723..95a34d6f51f63 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 e84c462ca8161..83bde1c50e23d 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 e44f66657955e..046a587205b51 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 3ab9b7f4083b8..8b15e10ba9cbf 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 edf47403c0d65..05738b6c48ae2 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 ab673c623b5d6..b8239ed88acca 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -800,6 +800,15 @@ impl 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 e3f82137ab3b2..08dadb4db851a 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 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 0000000000000..31208096151dc --- /dev/null +++ b/src/test/ui/tuple/builtin-fail.rs @@ -0,0 +1,19 @@ +#![feature(tuple_trait)] + +fn assert_is_tuple() {} + +struct TupleStruct(i32, i32); + +fn from_param_env() { + assert_is_tuple::(); + //~^ ERROR `T` is not a tuple +} + +fn main() { + assert_is_tuple::(); + //~^ ERROR `i32` is not a tuple + assert_is_tuple::<(i32)>(); + //~^ ERROR `i32` is not a tuple + assert_is_tuple::(); + //~^ 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 0000000000000..e3e29a73fdc01 --- /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::(); + | ^ 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() {} + | ^^^^^^^^^^^^^^^^^^ required by this bound in `assert_is_tuple` +help: consider restricting type parameter `T` + | +LL | fn from_param_env() { + | ++++++++++++++++++++ + +error[E0277]: `i32` is not a tuple + --> $DIR/builtin-fail.rs:13:23 + | +LL | assert_is_tuple::(); + | ^^^ 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() {} + | ^^^^^^^^^^^^^^^^^^ 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() {} + | ^^^^^^^^^^^^^^^^^^ 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::(); + | ^^^^^^^^^^^ 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() {} + | ^^^^^^^^^^^^^^^^^^ 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 0000000000000..d87ce526357e3 --- /dev/null +++ b/src/test/ui/tuple/builtin.rs @@ -0,0 +1,20 @@ +// check-pass + +#![feature(tuple_trait)] + +fn assert_is_tuple() {} + +struct Unsized([u8]); + +fn from_param_env() { + assert_is_tuple::(); +} + +fn main() { + assert_is_tuple::<()>(); + assert_is_tuple::<(i32,)>(); + assert_is_tuple::<(Unsized,)>(); + from_param_env::<()>(); + from_param_env::<(i32,)>(); + from_param_env::<(Unsized,)>(); +}