Auto merge of #114294 - matthiaskrgr:rollup-yk78pvd, r=matthiaskrgr

Rollup of 9 pull requests

Successful merges:

 - #114111 (Improve test case for experimental API remove_matches)
 - #114169 (refactor builtin unsize handling, extend comments)
 - #114182 (clean up after 113312)
 - #114193 (Update lexer emoji diagnostics to Unicode 15.0)
 - #114200 (Detect trait upcasting through struct tail unsizing in new solver select)
 - #114228 (Check lazy type aliases for well-formedness)
 - #114267 (Map RPITIT's opaque type bounds back from projections to opaques)
 - #114269 (Migrate GUI colors test to original CSS color format)
 - #114286 (Add missing feature gate in multiple_supertrait_upcastable doc)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2023-07-31 16:29:54 +00:00
commit b3df56a65f
27 changed files with 389 additions and 309 deletions

View File

@ -576,7 +576,6 @@ fn find_and_apply_rpit_args<'tcx>(
struct Visitor<'tcx> {
tcx: TyCtxt<'tcx>,
opaque: DefId,
function: DefId,
seen: FxHashSet<DefId>,
}
impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> 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:#?}");

View File

@ -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);
}
}

View File

@ -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<TyCtxt<'tcx>> 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
}
}
}

View File

@ -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);

View File

@ -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}"

View File

@ -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,

View File

@ -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,

View File

@ -10,6 +10,7 @@ declare_lint! {
/// ### Example
///
/// ```rust
/// #![feature(multiple_supertrait_upcastable)]
/// trait A {}
/// trait B {}
///

View File

@ -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.

View File

@ -1359,12 +1359,24 @@ impl<'tcx> ToPredicate<'tcx, Clause<'tcx>> for PolyTraitPredicate<'tcx> {
}
}
impl<'tcx> ToPredicate<'tcx> for OutlivesPredicate<ty::Region<'tcx>, 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<'tcx>, 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.

View File

@ -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<A> + Bar<B>` and `dyn Foo: Unsize<dyn Bar<_>>`
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 });
}
}

View File

@ -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();

View File

@ -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(),

View File

@ -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)> {

View File

@ -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))
}
}
}

View File

@ -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)
}
}

View File

@ -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<T>` -> `Struct<U>` where `T: Unsize<U>`
// `[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<T>` -> `Struct<U>` where `T: Unsize<U>`
(&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<TailB>` 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<U>`
// `(A, B, T)` -> `(A, B, U)` where `T: Unsize<U>`
(&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<dyn Super + 'a> for dyn Trait + 'b {}
/// // and impls removing auto trait bounds.
/// impl<'a, 'b: 'a> Unsize<dyn Trait + 'a> 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<ty::PolyExistentialPredicate<'tcx>>,
a_region: ty::Region<'tcx>,
b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
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<dyn Trait + 'a> for T {}
/// ```
fn consider_builtin_unsize_to_dyn(
&mut self,
goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
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<T: ?Sized, const N: usize> 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<T, U: ?Sized> {
/// sized_field: Vec<T>,
/// unsizable: Box<U>,
/// }
/// // results in the following builtin impl
/// impl<T: ?Sized, U: ?Sized, V: ?Sized> Unsize<Foo<T, V>> for Foo<T, U>
/// where
/// Box<U>: Unsize<Box<V>>,
/// {}
/// ```
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<TailB>` 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<T: ?Sized, U: ?Sized, V: ?Sized> Unsize<(T, V)> for (T, U)
/// where
/// U: Unsize<V>,
/// {}
/// ```
fn consider_builtin_tuple_unsize(
&mut self,
goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
a_tys: &'tcx ty::List<Ty<'tcx>>,
b_tys: &'tcx ty::List<Ty<'tcx>>,
) -> 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

View File

@ -181,8 +181,10 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitFinder<'_, 'tcx> {
fn visit_ty(&mut self, ty: Ty<'tcx>) -> std::ops::ControlFlow<Self::BreakTy> {
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

View File

@ -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

View File

@ -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: (

View File

@ -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)]

View File

@ -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`.

View File

@ -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)]
| ^^^^^^^^^^^^^^^

View File

@ -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<T: Mul> = <T as Mul>::Output;
fn main() {}

View File

@ -0,0 +1,8 @@
// Test that we check lazy type aliases for well-formedness.
#![feature(lazy_type_alias)]
#![allow(incomplete_features)]
type Alias<T> = <T as std::ops::Mul>::Output; //~ ERROR cannot multiply `T` by `T`
fn main() {}

View File

@ -0,0 +1,14 @@
error[E0277]: cannot multiply `T` by `T`
--> $DIR/unsatisfied-bounds-type-alias-body.rs:6:17
|
LL | type Alias<T> = <T as std::ops::Mul>::Output;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `T * T`
|
help: consider restricting type parameter `T`
|
LL | type Alias<T: std::ops::Mul> = <T as std::ops::Mul>::Output;
| +++++++++++++++
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.