diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 84452979aa5..73627a818e5 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -576,7 +576,6 @@ fn find_and_apply_rpit_args<'tcx>( struct Visitor<'tcx> { tcx: TyCtxt<'tcx>, opaque: DefId, - function: DefId, seen: FxHashSet, } impl<'tcx> ty::TypeVisitor> for Visitor<'tcx> { @@ -601,19 +600,6 @@ fn find_and_apply_rpit_args<'tcx>( } } } - ty::Alias(ty::Projection, alias) => { - if self.tcx.is_impl_trait_in_trait(alias.def_id) - && self.tcx.impl_trait_in_trait_parent_fn(alias.def_id) == self.function - { - // If we're lowering to associated item, install the opaque type which is just - // the `type_of` of the trait's associated item. If we're using the old lowering - // strategy, then just reinterpret the associated type like an opaque :^) - self.tcx - .type_of(alias.def_id) - .instantiate(self.tcx, alias.args) - .visit_with(self)?; - } - } ty::Alias(ty::Weak, alias) => { self.tcx .type_of(alias.def_id) @@ -627,7 +613,7 @@ fn find_and_apply_rpit_args<'tcx>( } } if let ControlFlow::Break(args) = - ret.visit_with(&mut Visitor { tcx, function, opaque, seen: Default::default() }) + ret.visit_with(&mut Visitor { tcx, opaque, seen: Default::default() }) { trace!(?args); trace!("expected: {hidden_ty:#?}"); diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 4df572f6199..ad886ce82b1 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -246,8 +246,11 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { // `ForeignItem`s are handled separately. hir::ItemKind::ForeignMod { .. } => {} hir::ItemKind::TyAlias(hir_ty, ..) => { - if tcx.type_of(item.owner_id.def_id).skip_binder().has_opaque_types() { - // Bounds are respected for `type X = impl Trait` and `type X = (impl Trait, Y);` + if tcx.features().lazy_type_alias + || tcx.type_of(item.owner_id).skip_binder().has_opaque_types() + { + // Bounds of lazy type aliases and of eager ones that contain opaque types are respected. + // E.g: `type X = impl Trait;`, `type X = (impl Trait, Y);`. check_item_type(tcx, def_id, hir_ty.span, UnsizedHandling::Allow); } } diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 758e9dc7230..4b7743fae53 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -3,7 +3,7 @@ use crate::astconv::{AstConv, PredicateFilter}; use rustc_hir as hir; use rustc_infer::traits::util; use rustc_middle::ty::GenericArgs; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeFolder}; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::Span; @@ -113,7 +113,7 @@ pub(super) fn explicit_item_bounds( .. }) => associated_type_bounds(tcx, def_id, bounds, *span), hir::Node::Item(hir::Item { - kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, .. }), + kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, in_trait: false, .. }), span, .. }) => { @@ -121,6 +121,27 @@ pub(super) fn explicit_item_bounds( let item_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args); opaque_type_bounds(tcx, def_id, bounds, item_ty, *span) } + // Since RPITITs are astconv'd as projections in `ast_ty_to_ty`, when we're asking + // for the item bounds of the *opaques* in a trait's default method signature, we + // need to map these projections back to opaques. + hir::Node::Item(hir::Item { + kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, in_trait: true, origin, .. }), + span, + .. + }) => { + let (hir::OpaqueTyOrigin::FnReturn(fn_def_id) + | hir::OpaqueTyOrigin::AsyncFn(fn_def_id)) = *origin + else { + bug!() + }; + let args = GenericArgs::identity_for_item(tcx, def_id); + let item_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args); + tcx.arena.alloc_slice( + &opaque_type_bounds(tcx, def_id, bounds, item_ty, *span) + .to_vec() + .fold_with(&mut AssocTyToOpaque { tcx, fn_def_id: fn_def_id.to_def_id() }), + ) + } hir::Node::Item(hir::Item { kind: hir::ItemKind::TyAlias(..), .. }) => &[], _ => bug!("item_bounds called on {:?}", def_id), }; @@ -135,3 +156,26 @@ pub(super) fn item_bounds( tcx.mk_clauses_from_iter(util::elaborate(tcx, bounds.iter().map(|&(bound, _span)| bound))) }) } + +struct AssocTyToOpaque<'tcx> { + tcx: TyCtxt<'tcx>, + fn_def_id: DefId, +} + +impl<'tcx> TypeFolder> for AssocTyToOpaque<'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if let ty::Alias(ty::Projection, projection_ty) = ty.kind() + && let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, .. }) + = self.tcx.opt_rpitit_info(projection_ty.def_id) + && fn_def_id == self.fn_def_id + { + self.tcx.type_of(projection_ty.def_id).instantiate(self.tcx, projection_ty.args) + } else { + ty + } + } +} diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index 119ed2fa408..d5f03a6aaed 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -558,10 +558,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let pred = clause.kind().rebind(match clause.kind().skip_binder() { ty::ClauseKind::Trait(trait_pred) => { - // FIXME(rpitit): This will need to be fixed when we move to associated types assert!(matches!( *trait_pred.trait_ref.self_ty().kind(), - ty::Alias(_, ty::AliasTy { def_id, args: alias_args, .. }) + ty::Alias(ty::Opaque, ty::AliasTy { def_id, args: alias_args, .. }) if def_id == rpit_def_id && args == alias_args )); ty::ClauseKind::Trait(trait_pred.with_self_ty(self.tcx, ty)) @@ -569,7 +568,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::ClauseKind::Projection(mut proj_pred) => { assert!(matches!( *proj_pred.projection_ty.self_ty().kind(), - ty::Alias(_, ty::AliasTy { def_id, args: alias_args, .. }) + ty::Alias(ty::Opaque, ty::AliasTy { def_id, args: alias_args, .. }) if def_id == rpit_def_id && args == alias_args )); proj_pred = proj_pred.with_self_ty(self.tcx, ty); diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index ca3b595d238..0624a4baf79 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -723,11 +723,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .iter_instantiated_copied(self.tcx, args) .find_map(|(p, s)| get_future_output(p.as_predicate(), s))?, ty::Error(_) => return None, - ty::Alias(ty::Projection, proj) if self.tcx.is_impl_trait_in_trait(proj.def_id) => self - .tcx - .explicit_item_bounds(proj.def_id) - .iter_instantiated_copied(self.tcx, proj.args) - .find_map(|(p, s)| get_future_output(p.as_predicate(), s))?, _ => span_bug!( self.tcx.def_span(expr_def_id), "async fn generator return type not an inference variable: {ret_ty}" diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index 945136fbff2..9c90b704586 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -619,13 +619,6 @@ impl<'tcx> InferCtxt<'tcx> { { hidden_ty } - // FIXME(RPITIT): This can go away when we move to associated types - // FIXME(inherent_associated_types): Extend this to support `ty::Inherent`, too. - ty::Alias(ty::Projection, ty::AliasTy { def_id: def_id2, args: args2, .. }) - if def_id == def_id2 && args == args2 => - { - hidden_ty - } _ => ty, }, lt_op: |lt| lt, diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index ba05622bf37..e6917f4b2d3 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1458,15 +1458,20 @@ impl TypeAliasBounds { impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - let hir::ItemKind::TyAlias(ty, type_alias_generics) = &item.kind else { return }; - if cx.tcx.type_of(item.owner_id.def_id).skip_binder().has_opaque_types() { - // Bounds are respected for `type X = impl Trait` and `type X = (impl Trait, Y);` + let hir::ItemKind::TyAlias(hir_ty, type_alias_generics) = &item.kind else { return }; + + if cx.tcx.features().lazy_type_alias { + // Bounds of lazy type aliases are respected. return; } - if cx.tcx.type_of(item.owner_id).skip_binder().has_inherent_projections() { - // Bounds are respected for `type X = … Type::Inherent …` + + let ty = cx.tcx.type_of(item.owner_id).skip_binder(); + if ty.has_opaque_types() || ty.has_inherent_projections() { + // Bounds of type aliases that contain opaque types or inherent projections are respected. + // E.g: `type X = impl Trait;`, `type X = (impl Trait, Y);`, `type X = Type::Inherent;`. return; } + // There must not be a where clause if type_alias_generics.predicates.is_empty() { return; @@ -1491,7 +1496,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { if !where_spans.is_empty() { let sub = (!suggested_changing_assoc_types).then(|| { suggested_changing_assoc_types = true; - SuggestChangingAssocTypes { ty } + SuggestChangingAssocTypes { ty: hir_ty } }); cx.emit_spanned_lint( TYPE_ALIAS_BOUNDS, @@ -1507,7 +1512,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { let suggestion = BuiltinTypeAliasGenericBoundsSuggestion { suggestions: inline_sugg }; let sub = (!suggested_changing_assoc_types).then(|| { suggested_changing_assoc_types = true; - SuggestChangingAssocTypes { ty } + SuggestChangingAssocTypes { ty: hir_ty } }); cx.emit_spanned_lint( TYPE_ALIAS_BOUNDS, diff --git a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs index 53fe0ceb234..84558ee1f02 100644 --- a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs +++ b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs @@ -10,6 +10,7 @@ declare_lint! { /// ### Example /// /// ```rust + /// #![feature(multiple_supertrait_upcastable)] /// trait A {} /// trait B {} /// diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs index 73b332fd8ec..b21a00e4122 100644 --- a/compiler/rustc_middle/src/traits/solve.rs +++ b/compiler/rustc_middle/src/traits/solve.rs @@ -57,6 +57,7 @@ pub enum Certainty { impl Certainty { pub const AMBIGUOUS: Certainty = Certainty::Maybe(MaybeCause::Ambiguity); + pub const OVERFLOW: Certainty = Certainty::Maybe(MaybeCause::Overflow); /// Use this function to merge the certainty of multiple nested subgoals. /// @@ -66,7 +67,7 @@ impl Certainty { /// success, we merge these two responses. This results in ambiguity. /// /// If we unify ambiguity with overflow, we return overflow. This doesn't matter - /// inside of the solver as we distinguish ambiguity from overflow. It does + /// inside of the solver as we do not distinguish ambiguity from overflow. It does /// however matter for diagnostics. If `T: Foo` resulted in overflow and `T: Bar` /// in ambiguity without changing the inference state, we still want to tell the /// user that `T: Baz` results in overflow. diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index bbf8a645f15..2b4c834f766 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1359,12 +1359,24 @@ impl<'tcx> ToPredicate<'tcx, Clause<'tcx>> for PolyTraitPredicate<'tcx> { } } +impl<'tcx> ToPredicate<'tcx> for OutlivesPredicate, ty::Region<'tcx>> { + fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { + ty::Binder::dummy(PredicateKind::Clause(ClauseKind::RegionOutlives(self))).to_predicate(tcx) + } +} + impl<'tcx> ToPredicate<'tcx> for PolyRegionOutlivesPredicate<'tcx> { fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { self.map_bound(|p| PredicateKind::Clause(ClauseKind::RegionOutlives(p))).to_predicate(tcx) } } +impl<'tcx> ToPredicate<'tcx> for OutlivesPredicate, ty::Region<'tcx>> { + fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { + ty::Binder::dummy(PredicateKind::Clause(ClauseKind::TypeOutlives(self))).to_predicate(tcx) + } +} + impl<'tcx> ToPredicate<'tcx> for PolyTypeOutlivesPredicate<'tcx> { fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { self.map_bound(|p| PredicateKind::Clause(ClauseKind::TypeOutlives(p))).to_predicate(tcx) @@ -2613,19 +2625,6 @@ impl<'tcx> TyCtxt<'tcx> { matches!(self.trait_of_item(def_id), Some(trait_id) if self.has_attr(trait_id, sym::const_trait)) } - pub fn impl_trait_in_trait_parent_fn(self, mut def_id: DefId) -> DefId { - match self.opt_rpitit_info(def_id) { - Some(ImplTraitInTraitData::Trait { fn_def_id, .. }) - | Some(ImplTraitInTraitData::Impl { fn_def_id, .. }) => fn_def_id, - None => { - while let def_kind = self.def_kind(def_id) && def_kind != DefKind::AssocFn { - def_id = self.parent(def_id); - } - def_id - } - } - } - /// Returns the `DefId` of the item within which the `impl Trait` is declared. /// For type-alias-impl-trait this is the `type` alias. /// For impl-trait-in-assoc-type this is the assoc type. diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index ab90db6ff58..5ec48c7ac57 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -7,7 +7,7 @@ use rustc_hir::def_id::DefId; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::Reveal; use rustc_middle::traits::solve::inspect::CandidateKind; -use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult}; +use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; use rustc_middle::traits::BuiltinImplSource; use rustc_middle::ty::fast_reject::{SimplifiedType, TreatParams}; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -299,7 +299,7 @@ pub(super) trait GoalKind<'tcx>: /// for unsize coercion in hir typeck and because it is difficult to /// otherwise recompute this for codegen. This is a bit of a mess but the /// easiest way to maintain the existing behavior for now. - fn consider_builtin_unsize_and_upcast_candidates( + fn consider_builtin_unsize_candidates( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)>; @@ -402,7 +402,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ecx.with_incremented_depth( |ecx| { let result = ecx.evaluate_added_goals_and_make_canonical_response( - Certainty::Maybe(MaybeCause::Overflow), + Certainty::OVERFLOW, )?; Ok(vec![Candidate { source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), @@ -624,7 +624,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // There may be multiple unsize candidates for a trait with several supertraits: // `trait Foo: Bar + Bar` and `dyn Foo: Unsize>` if lang_items.unsize_trait() == Some(trait_def_id) { - for (result, source) in G::consider_builtin_unsize_and_upcast_candidates(self, goal) { + for (result, source) in G::consider_builtin_unsize_candidates(self, goal) { candidates.push(Candidate { source: CandidateSource::BuiltinImpl(source), result }); } } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index 9e3b0bbc4fb..485bc912c44 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -11,8 +11,8 @@ use rustc_infer::traits::ObligationCause; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::traits::solve::inspect; use rustc_middle::traits::solve::{ - CanonicalInput, CanonicalResponse, Certainty, IsNormalizesToHack, MaybeCause, - PredefinedOpaques, PredefinedOpaquesData, QueryResult, + CanonicalInput, CanonicalResponse, Certainty, IsNormalizesToHack, PredefinedOpaques, + PredefinedOpaquesData, QueryResult, }; use rustc_middle::traits::DefiningAnchor; use rustc_middle::ty::{ @@ -475,7 +475,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { let mut new_goals = NestedGoals::new(); let response = self.repeat_while_none( - |_| Ok(Certainty::Maybe(MaybeCause::Overflow)), + |_| Ok(Certainty::OVERFLOW), |this| { this.inspect.evaluate_added_goals_loop_start(); diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs index 6045001510e..409bcf5ce61 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs @@ -338,7 +338,7 @@ fn rematch_unsize<'tcx>( .into_obligations(), ); - // Similar to ADTs, require that the rest of the fields are equal. + // Similar to ADTs, require that we can unsize the tail. nested.push(Obligation::new( tcx, ObligationCause::dummy(), diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 03ade738bb6..2ce57751740 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -503,7 +503,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ) } - fn consider_builtin_unsize_and_upcast_candidates( + fn consider_builtin_unsize_candidates( _ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> { diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs index 98c8a74752c..c25d6eca918 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs @@ -9,9 +9,7 @@ use cache::ProvisionalCache; use overflow::OverflowData; use rustc_index::IndexVec; use rustc_middle::dep_graph::DepKind; -use rustc_middle::traits::solve::{ - CanonicalInput, Certainty, EvaluationCache, MaybeCause, QueryResult, -}; +use rustc_middle::traits::solve::{CanonicalInput, Certainty, EvaluationCache, QueryResult}; use rustc_middle::ty::TyCtxt; use std::{collections::hash_map::Entry, mem}; @@ -146,11 +144,7 @@ impl<'tcx> SearchGraph<'tcx> { { Err(cache.provisional_result(entry_index)) } else { - Err(super::response_no_constraints( - tcx, - input, - Certainty::Maybe(MaybeCause::Overflow), - )) + Err(super::response_no_constraints(tcx, input, Certainty::OVERFLOW)) } } } diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs b/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs index e0a2e0c5cc2..83a051eeb1a 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs @@ -1,6 +1,6 @@ use rustc_infer::infer::canonical::Canonical; use rustc_infer::traits::query::NoSolution; -use rustc_middle::traits::solve::{Certainty, MaybeCause, QueryResult}; +use rustc_middle::traits::solve::{Certainty, QueryResult}; use rustc_middle::ty::TyCtxt; use rustc_session::Limit; @@ -115,6 +115,6 @@ impl<'tcx> SearchGraph<'tcx> { goal: Canonical<'tcx, impl Sized>, ) -> QueryResult<'tcx> { self.overflow_data.deal_with_overflow(); - response_no_constraints(tcx, goal, Certainty::Maybe(MaybeCause::Overflow)) + response_no_constraints(tcx, goal, Certainty::OVERFLOW) } } diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 40ce4a3b3e4..14a5b9656e5 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -7,7 +7,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::{LangItem, Movability}; use rustc_infer::traits::query::NoSolution; use rustc_middle::traits::solve::inspect::CandidateKind; -use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult}; +use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; use rustc_middle::traits::{BuiltinImplSource, Reveal}; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections}; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; @@ -366,69 +366,6 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ) } - fn consider_builtin_unsize_and_upcast_candidates( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { - return vec![]; - } - - ecx.probe(|_| CandidateKind::DynUpcastingAssembly).enter(|ecx| { - let a_ty = goal.predicate.self_ty(); - // We need to normalize the b_ty since it's matched structurally - // in the other functions below. - let b_ty = match ecx - .normalize_non_self_ty(goal.predicate.trait_ref.args.type_at(1), goal.param_env) - { - Ok(Some(b_ty)) => { - // If we have a type var, then bail with ambiguity. - if b_ty.is_ty_var() { - return vec![( - ecx.evaluate_added_goals_and_make_canonical_response( - Certainty::AMBIGUOUS, - ) - .unwrap(), - BuiltinImplSource::Misc, - )]; - } else { - b_ty - } - } - Ok(None) => { - return vec![( - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe( - MaybeCause::Overflow, - )) - .unwrap(), - BuiltinImplSource::Misc, - )]; - } - Err(_) => return vec![], - }; - - let mut results = vec![]; - results.extend(ecx.consider_builtin_dyn_upcast_candidates(goal.param_env, a_ty, b_ty)); - results.extend( - ecx.consider_builtin_unsize_candidate(goal.with(ecx.tcx(), (a_ty, b_ty))) - .into_iter() - .map(|resp| { - // If we're unsizing from tuple -> tuple, detect - let source = - if matches!((a_ty.kind(), b_ty.kind()), (ty::Tuple(..), ty::Tuple(..))) - { - BuiltinImplSource::TupleUnsizing - } else { - BuiltinImplSource::Misc - }; - (resp, source) - }), - ); - - results - }) - } - fn consider_builtin_discriminant_kind_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -486,153 +423,111 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { )?; ecx.evaluate_added_goals_and_make_canonical_response(certainty) } -} -impl<'tcx> EvalCtxt<'_, 'tcx> { - fn consider_builtin_unsize_candidate( - &mut self, - goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, - ) -> QueryResult<'tcx> { - let Goal { param_env, predicate: (a_ty, b_ty) } = goal; - self.probe_candidate("builtin unsize").enter(|ecx| { - let tcx = ecx.tcx(); + fn consider_builtin_unsize_candidates( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> { + if goal.predicate.polarity != ty::ImplPolarity::Positive { + return vec![]; + } + + let misc_candidate = |ecx: &mut EvalCtxt<'_, 'tcx>, certainty| { + ( + ecx.evaluate_added_goals_and_make_canonical_response(certainty).unwrap(), + BuiltinImplSource::Misc, + ) + }; + + let result_to_single = |result, source| match result { + Ok(resp) => vec![(resp, source)], + Err(NoSolution) => vec![], + }; + + ecx.probe(|_| CandidateKind::DynUpcastingAssembly).enter(|ecx| { + let a_ty = goal.predicate.self_ty(); + // We need to normalize the b_ty since it's matched structurally + // in the other functions below. + let b_ty = match ecx + .normalize_non_self_ty(goal.predicate.trait_ref.args.type_at(1), goal.param_env) + { + Ok(Some(b_ty)) => b_ty, + Ok(None) => return vec![misc_candidate(ecx, Certainty::OVERFLOW)], + Err(_) => return vec![], + }; + + let goal = goal.with(ecx.tcx(), (a_ty, b_ty)); match (a_ty.kind(), b_ty.kind()) { - (ty::Infer(ty::TyVar(_)), _) | (_, ty::Infer(ty::TyVar(_))) => { - bug!("unexpected type variable in unsize goal") - } - // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b` - (&ty::Dynamic(_, _, ty::Dyn), &ty::Dynamic(_, _, ty::Dyn)) => { - // Dyn upcasting is handled separately, since due to upcasting, - // when there are two supertraits that differ by args, we - // may return more than one query response. - Err(NoSolution) - } + (ty::Infer(ty::TyVar(..)), ..) => bug!("unexpected infer {a_ty:?} {b_ty:?}"), + (_, ty::Infer(ty::TyVar(..))) => vec![misc_candidate(ecx, Certainty::AMBIGUOUS)], + + // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`. + ( + &ty::Dynamic(a_data, a_region, ty::Dyn), + &ty::Dynamic(b_data, b_region, ty::Dyn), + ) => ecx.consider_builtin_dyn_upcast_candidates( + goal, a_data, a_region, b_data, b_region, + ), + // `T` -> `dyn Trait` unsizing - (_, &ty::Dynamic(data, region, ty::Dyn)) => { - // Can only unsize to an object-safe type - if data - .principal_def_id() - .is_some_and(|def_id| !tcx.check_is_object_safe(def_id)) - { - return Err(NoSolution); - } + (_, &ty::Dynamic(b_data, b_region, ty::Dyn)) => result_to_single( + ecx.consider_builtin_unsize_to_dyn(goal, b_data, b_region), + BuiltinImplSource::Misc, + ), - let Some(sized_def_id) = tcx.lang_items().sized_trait() else { - return Err(NoSolution); - }; - // Check that the type implements all of the predicates of the def-id. - // (i.e. the principal, all of the associated types match, and any auto traits) - ecx.add_goals( - data.iter() - .map(|pred| Goal::new(tcx, param_env, pred.with_self_ty(tcx, a_ty))), - ); - // The type must be Sized to be unsized. - ecx.add_goal(Goal::new( - tcx, - param_env, - ty::TraitRef::new(tcx, sized_def_id, [a_ty]), - )); - // The type must outlive the lifetime of the `dyn` we're unsizing into. - ecx.add_goal(Goal::new( - tcx, - param_env, - ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region)), - )); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } - // `[T; n]` -> `[T]` unsizing - (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => { - // We just require that the element type stays the same - ecx.eq(param_env, a_elem_ty, b_elem_ty)?; - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } - // Struct unsizing `Struct` -> `Struct` where `T: Unsize` + // `[T; N]` -> `[T]` unsizing + (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => result_to_single( + ecx.consider_builtin_array_unsize(goal, a_elem_ty, b_elem_ty), + BuiltinImplSource::Misc, + ), + + // `Struct` -> `Struct` where `T: Unsize` (&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args)) - if a_def.is_struct() && a_def.did() == b_def.did() => + if a_def.is_struct() && a_def == b_def => { - let unsizing_params = tcx.unsizing_params_for_adt(a_def.did()); - // We must be unsizing some type parameters. This also implies - // that the struct has a tail field. - if unsizing_params.is_empty() { - return Err(NoSolution); - } - - let tail_field = a_def.non_enum_variant().tail(); - let tail_field_ty = tcx.type_of(tail_field.did); - - let a_tail_ty = tail_field_ty.instantiate(tcx, a_args); - let b_tail_ty = tail_field_ty.instantiate(tcx, b_args); - - // Substitute just the unsizing params from B into A. The type after - // this substitution must be equal to B. This is so we don't unsize - // unrelated type parameters. - let new_a_args = - tcx.mk_args_from_iter(a_args.iter().enumerate().map(|(i, a)| { - if unsizing_params.contains(i as u32) { b_args[i] } else { a } - })); - let unsized_a_ty = Ty::new_adt(tcx, a_def, new_a_args); - - // Finally, we require that `TailA: Unsize` for the tail field - // types. - ecx.eq(param_env, unsized_a_ty, b_ty)?; - ecx.add_goal(Goal::new( - tcx, - param_env, - ty::TraitRef::new( - tcx, - tcx.lang_items().unsize_trait().unwrap(), - [a_tail_ty, b_tail_ty], - ), - )); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + result_to_single( + ecx.consider_builtin_struct_unsize(goal, a_def, a_args, b_args), + BuiltinImplSource::Misc, + ) } - // Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize` + + // `(A, B, T)` -> `(A, B, U)` where `T: Unsize` (&ty::Tuple(a_tys), &ty::Tuple(b_tys)) if a_tys.len() == b_tys.len() && !a_tys.is_empty() => { - let (a_last_ty, a_rest_tys) = a_tys.split_last().unwrap(); - let b_last_ty = b_tys.last().unwrap(); - - // Substitute just the tail field of B., and require that they're equal. - let unsized_a_ty = - Ty::new_tup_from_iter(tcx, a_rest_tys.iter().chain([b_last_ty]).copied()); - ecx.eq(param_env, unsized_a_ty, b_ty)?; - - // Similar to ADTs, require that the rest of the fields are equal. - ecx.add_goal(Goal::new( - tcx, - param_env, - ty::TraitRef::new( - tcx, - tcx.lang_items().unsize_trait().unwrap(), - [*a_last_ty, *b_last_ty], - ), - )); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + result_to_single( + ecx.consider_builtin_tuple_unsize(goal, a_tys, b_tys), + BuiltinImplSource::TupleUnsizing, + ) } - _ => Err(NoSolution), + + _ => vec![], } }) } +} +impl<'tcx> EvalCtxt<'_, 'tcx> { + /// Trait upcasting allows for coercions between trait objects: + /// ```ignore (builtin impl example) + /// trait Super {} + /// trait Trait: Super {} + /// // results in builtin impls upcasting to a super trait + /// impl<'a, 'b: 'a> Unsize for dyn Trait + 'b {} + /// // and impls removing auto trait bounds. + /// impl<'a, 'b: 'a> Unsize for dyn Trait + Send + 'b {} + /// ``` fn consider_builtin_dyn_upcast_candidates( &mut self, - param_env: ty::ParamEnv<'tcx>, - a_ty: Ty<'tcx>, - b_ty: Ty<'tcx>, + goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, + a_data: &'tcx ty::List>, + a_region: ty::Region<'tcx>, + b_data: &'tcx ty::List>, + b_region: ty::Region<'tcx>, ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> { - if a_ty.is_ty_var() || b_ty.is_ty_var() { - bug!("unexpected type variable in unsize goal") - } - - let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else { - return vec![]; - }; - let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else { - return vec![]; - }; - let tcx = self.tcx(); + let Goal { predicate: (a_ty, b_ty), .. } = goal; + // All of a's auto traits need to be in b's auto traits. let auto_traits_compatible = b_data.auto_traits().all(|b| a_data.auto_traits().any(|a| a == b)); @@ -665,12 +560,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn); // We also require that A's lifetime outlives B's lifetime. - ecx.eq(param_env, new_a_ty, b_ty)?; - ecx.add_goal(Goal::new( - tcx, - param_env, - ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)), - )); + ecx.eq(goal.param_env, new_a_ty, b_ty)?; + ecx.add_goal(goal.with(tcx, ty::OutlivesPredicate(a_region, b_region))); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }, ) @@ -703,6 +594,161 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { responses } + /// ```ignore (builtin impl example) + /// trait Trait { + /// fn foo(&self); + /// } + /// // results in the following builtin impl + /// impl<'a, T: Trait + 'a> Unsize for T {} + /// ``` + fn consider_builtin_unsize_to_dyn( + &mut self, + goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, + b_data: &'tcx ty::List>, + b_region: ty::Region<'tcx>, + ) -> QueryResult<'tcx> { + let tcx = self.tcx(); + let Goal { predicate: (a_ty, _b_ty), .. } = goal; + + // Can only unsize to an object-safe trait + if b_data.principal_def_id().is_some_and(|def_id| !tcx.check_is_object_safe(def_id)) { + return Err(NoSolution); + } + + // Check that the type implements all of the predicates of the trait object. + // (i.e. the principal, all of the associated types match, and any auto traits) + self.add_goals(b_data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty)))); + + // The type must be `Sized` to be unsized. + if let Some(sized_def_id) = tcx.lang_items().sized_trait() { + self.add_goal(goal.with(tcx, ty::TraitRef::new(tcx, sized_def_id, [a_ty]))); + } else { + return Err(NoSolution); + } + + // The type must outlive the lifetime of the `dyn` we're unsizing into. + self.add_goal(goal.with(tcx, ty::OutlivesPredicate(a_ty, b_region))); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + + /// We have the following builtin impls for arrays: + /// ```ignore (builtin impl example) + /// impl Unsize<[T]> for [T; N] {} + /// ``` + /// While the impl itself could theoretically not be builtin, + /// the actual unsizing behavior is builtin. Its also easier to + /// make all impls of `Unsize` builtin as we're able to use + /// `#[rustc_deny_explicit_impl]` in this case. + fn consider_builtin_array_unsize( + &mut self, + goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, + a_elem_ty: Ty<'tcx>, + b_elem_ty: Ty<'tcx>, + ) -> QueryResult<'tcx> { + self.eq(goal.param_env, a_elem_ty, b_elem_ty)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + + /// We generate a builtin `Unsize` impls for structs with generic parameters only + /// mentioned by the last field. + /// ```ignore (builtin impl example) + /// struct Foo { + /// sized_field: Vec, + /// unsizable: Box, + /// } + /// // results in the following builtin impl + /// impl Unsize> for Foo + /// where + /// Box: Unsize>, + /// {} + /// ``` + fn consider_builtin_struct_unsize( + &mut self, + goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, + def: ty::AdtDef<'tcx>, + a_args: ty::GenericArgsRef<'tcx>, + b_args: ty::GenericArgsRef<'tcx>, + ) -> QueryResult<'tcx> { + let tcx = self.tcx(); + let Goal { predicate: (_a_ty, b_ty), .. } = goal; + + let unsizing_params = tcx.unsizing_params_for_adt(def.did()); + // We must be unsizing some type parameters. This also implies + // that the struct has a tail field. + if unsizing_params.is_empty() { + return Err(NoSolution); + } + + let tail_field = def.non_enum_variant().tail(); + let tail_field_ty = tcx.type_of(tail_field.did); + + let a_tail_ty = tail_field_ty.instantiate(tcx, a_args); + let b_tail_ty = tail_field_ty.instantiate(tcx, b_args); + + // Substitute just the unsizing params from B into A. The type after + // this substitution must be equal to B. This is so we don't unsize + // unrelated type parameters. + let new_a_args = tcx.mk_args_from_iter( + a_args + .iter() + .enumerate() + .map(|(i, a)| if unsizing_params.contains(i as u32) { b_args[i] } else { a }), + ); + let unsized_a_ty = Ty::new_adt(tcx, def, new_a_args); + + // Finally, we require that `TailA: Unsize` for the tail field + // types. + self.eq(goal.param_env, unsized_a_ty, b_ty)?; + self.add_goal(goal.with( + tcx, + ty::TraitRef::new( + tcx, + tcx.lang_items().unsize_trait().unwrap(), + [a_tail_ty, b_tail_ty], + ), + )); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + + /// We generate the following builtin impl for tuples of all sizes. + /// + /// This impl is still unstable and we emit a feature error when it + /// when it is used by a coercion. + /// ```ignore (builtin impl example) + /// impl Unsize<(T, V)> for (T, U) + /// where + /// U: Unsize, + /// {} + /// ``` + fn consider_builtin_tuple_unsize( + &mut self, + goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, + a_tys: &'tcx ty::List>, + b_tys: &'tcx ty::List>, + ) -> QueryResult<'tcx> { + let tcx = self.tcx(); + let Goal { predicate: (_a_ty, b_ty), .. } = goal; + + let (&a_last_ty, a_rest_tys) = a_tys.split_last().unwrap(); + let &b_last_ty = b_tys.last().unwrap(); + + // Substitute just the tail field of B., and require that they're equal. + let unsized_a_ty = + Ty::new_tup_from_iter(tcx, a_rest_tys.iter().copied().chain([b_last_ty])); + self.eq(goal.param_env, unsized_a_ty, b_ty)?; + + // Similar to ADTs, require that we can unsize the tail. + self.add_goal(goal.with( + tcx, + ty::TraitRef::new( + tcx, + tcx.lang_items().unsize_trait().unwrap(), + [a_last_ty, b_last_ty], + ), + )); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + // Return `Some` if there is an impl (built-in or user provided) that may // hold for the self type of the goal, which for coherence and soundness // purposes must disqualify the built-in auto impl assembled by considering diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 4c896712b1b..791a1f4fa99 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -181,8 +181,10 @@ impl<'tcx> TypeVisitor> for ImplTraitInTraitFinder<'_, 'tcx> { fn visit_ty(&mut self, ty: Ty<'tcx>) -> std::ops::ControlFlow { if let ty::Alias(ty::Projection, unshifted_alias_ty) = *ty.kind() - && self.tcx.is_impl_trait_in_trait(unshifted_alias_ty.def_id) - && self.tcx.impl_trait_in_trait_parent_fn(unshifted_alias_ty.def_id) == self.fn_def_id + && let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, .. } + | ty::ImplTraitInTraitData::Impl { fn_def_id, .. }) + = self.tcx.opt_rpitit_info(unshifted_alias_ty.def_id) + && fn_def_id == self.fn_def_id && self.seen.insert(unshifted_alias_ty.def_id) { // We have entered some binders as we've walked into the diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index a1822c83986..44c7c07d3a0 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -11,7 +11,7 @@ use std::path::{Path, PathBuf}; const ENTRY_LIMIT: usize = 900; // FIXME: The following limits should be reduced eventually. const ISSUES_ENTRY_LIMIT: usize = 1893; -const ROOT_ENTRY_LIMIT: usize = 872; +const ROOT_ENTRY_LIMIT: usize = 873; const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[ "rs", // test source files diff --git a/tests/rustdoc-gui/rust-logo.goml b/tests/rustdoc-gui/rust-logo.goml index 640ed152b0d..cd453aea276 100644 --- a/tests/rustdoc-gui/rust-logo.goml +++ b/tests/rustdoc-gui/rust-logo.goml @@ -33,20 +33,20 @@ call-function: ( "check-logo", { "theme": "ayu", - "filter": "drop-shadow(rgb(255, 255, 255) 1px 0px 0px) " + - "drop-shadow(rgb(255, 255, 255) 0px 1px 0px) " + - "drop-shadow(rgb(255, 255, 255) -1px 0px 0px) " + - "drop-shadow(rgb(255, 255, 255) 0px -1px 0px)", + "filter": "drop-shadow(#fff 1px 0px 0px) " + + "drop-shadow(#fff 0px 1px 0px) " + + "drop-shadow(#fff -1px 0px 0px) " + + "drop-shadow(#fff 0px -1px 0px)", }, ) call-function: ( "check-logo", { "theme": "dark", - "filter": "drop-shadow(rgb(255, 255, 255) 1px 0px 0px) " + - "drop-shadow(rgb(255, 255, 255) 0px 1px 0px) " + - "drop-shadow(rgb(255, 255, 255) -1px 0px 0px) " + - "drop-shadow(rgb(255, 255, 255) 0px -1px 0px)", + "filter": "drop-shadow(#fff 1px 0px 0px) " + + "drop-shadow(#fff 0px 1px 0px) " + + "drop-shadow(#fff -1px 0px 0px) " + + "drop-shadow(#fff 0px -1px 0px)", }, ) call-function: ( diff --git a/tests/ui/impl-trait/in-trait/default-body-with-rpit.rs b/tests/ui/impl-trait/in-trait/default-body-with-rpit.rs index 0558d95128f..25133214dc6 100644 --- a/tests/ui/impl-trait/in-trait/default-body-with-rpit.rs +++ b/tests/ui/impl-trait/in-trait/default-body-with-rpit.rs @@ -1,5 +1,5 @@ // edition:2021 -// known-bug: #108304 +// check-pass #![feature(async_fn_in_trait, return_position_impl_trait_in_trait)] #![allow(incomplete_features)] diff --git a/tests/ui/impl-trait/in-trait/default-body-with-rpit.stderr b/tests/ui/impl-trait/in-trait/default-body-with-rpit.stderr deleted file mode 100644 index b5fc9d44d36..00000000000 --- a/tests/ui/impl-trait/in-trait/default-body-with-rpit.stderr +++ /dev/null @@ -1,24 +0,0 @@ -error: concrete type differs from previous defining opaque type use - --> $DIR/default-body-with-rpit.rs:11:9 - | -LL | "" - | ^^ expected `impl Debug`, got `&'static str` - | -note: previous use here - --> $DIR/default-body-with-rpit.rs:10:39 - | -LL | async fn baz(&self) -> impl Debug { - | _______________________________________^ -LL | | "" -LL | | } - | |_____^ - -error[E0720]: cannot resolve opaque type - --> $DIR/default-body-with-rpit.rs:10:28 - | -LL | async fn baz(&self) -> impl Debug { - | ^^^^^^^^^^ cannot resolve opaque type - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0720`. diff --git a/tests/ui/type-alias/lazy-type-alias-enum-variant.rs b/tests/ui/lazy-type-alias/enum-variant.rs similarity index 100% rename from tests/ui/type-alias/lazy-type-alias-enum-variant.rs rename to tests/ui/lazy-type-alias/enum-variant.rs diff --git a/tests/ui/type-alias/lazy-type-alias-enum-variant.stderr b/tests/ui/lazy-type-alias/enum-variant.stderr similarity index 88% rename from tests/ui/type-alias/lazy-type-alias-enum-variant.stderr rename to tests/ui/lazy-type-alias/enum-variant.stderr index 381261b95c7..4360db91778 100644 --- a/tests/ui/type-alias/lazy-type-alias-enum-variant.stderr +++ b/tests/ui/lazy-type-alias/enum-variant.stderr @@ -1,5 +1,5 @@ warning: the feature `lazy_type_alias` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/lazy-type-alias-enum-variant.rs:4:12 + --> $DIR/enum-variant.rs:4:12 | LL | #![feature(lazy_type_alias)] | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/lazy-type-alias/type-alias-bounds-are-enforced.rs b/tests/ui/lazy-type-alias/type-alias-bounds-are-enforced.rs new file mode 100644 index 00000000000..d0abd3ebf24 --- /dev/null +++ b/tests/ui/lazy-type-alias/type-alias-bounds-are-enforced.rs @@ -0,0 +1,14 @@ +// Check that we don't issue the lint `type_alias_bounds` for +// lazy type aliases since the bounds are indeed enforced. + +// check-pass + +#![feature(lazy_type_alias)] +#![allow(incomplete_features)] +#![deny(type_alias_bounds)] + +use std::ops::Mul; + +type Alias = ::Output; + +fn main() {} diff --git a/tests/ui/lazy-type-alias/unsatisfied-bounds-type-alias-body.rs b/tests/ui/lazy-type-alias/unsatisfied-bounds-type-alias-body.rs new file mode 100644 index 00000000000..c798e4e4368 --- /dev/null +++ b/tests/ui/lazy-type-alias/unsatisfied-bounds-type-alias-body.rs @@ -0,0 +1,8 @@ +// Test that we check lazy type aliases for well-formedness. + +#![feature(lazy_type_alias)] +#![allow(incomplete_features)] + +type Alias = ::Output; //~ ERROR cannot multiply `T` by `T` + +fn main() {} diff --git a/tests/ui/lazy-type-alias/unsatisfied-bounds-type-alias-body.stderr b/tests/ui/lazy-type-alias/unsatisfied-bounds-type-alias-body.stderr new file mode 100644 index 00000000000..d022f825140 --- /dev/null +++ b/tests/ui/lazy-type-alias/unsatisfied-bounds-type-alias-body.stderr @@ -0,0 +1,14 @@ +error[E0277]: cannot multiply `T` by `T` + --> $DIR/unsatisfied-bounds-type-alias-body.rs:6:17 + | +LL | type Alias = ::Output; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `T * T` + | +help: consider restricting type parameter `T` + | +LL | type Alias = ::Output; + | +++++++++++++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`.