Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion compiler/rustc_const_eval/src/interpret/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
};

let (infcx, param_env) =
self.tcx.infer_ctxt().build_with_typing_env(self.typing_env);
self.tcx.infer_ctxt().build_with_typing_env(ty::TypingEnv {
typing_mode: ty::TypingMode::Reflection,
..self.typing_env
});

let ocx = ObligationCtxt::new(&infcx);
ocx.register_obligations(preds.iter().map(|pred: PolyExistentialPredicate<'_>| {
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@ language_item_table! {
CoerceUnsized, sym::coerce_unsized, coerce_unsized_trait, Target::Trait, GenericRequirement::Minimum(1);
DispatchFromDyn, sym::dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1);

TryAsDyn, sym::try_as_dyn, try_as_dyn, Target::Trait, GenericRequirement::Exact(1);

// lang items relating to transmutability
TransmuteOpts, sym::transmute_opts, transmute_opts, Target::Struct, GenericRequirement::Exact(0);
TransmuteTrait, sym::transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(2);
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ pub(crate) fn provide(providers: &mut Providers) {
adt_def,
fn_sig,
impl_trait_header,
impl_is_fully_generic_for_reflection,
coroutine_kind,
coroutine_for_closure,
opaque_ty_origin,
Expand Down Expand Up @@ -1274,6 +1275,11 @@ pub fn suggest_impl_trait<'tcx>(
None
}

fn impl_is_fully_generic_for_reflection(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
tcx.impl_trait_header(def_id).is_fully_generic_for_reflection()
&& tcx.explicit_predicates_of(def_id).is_fully_generic_for_reflection()
}

fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::ImplTraitHeader<'_> {
let icx = ItemCtxt::new(tcx, def_id);
let item = tcx.hir_expect_item(def_id);
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1047,6 +1047,7 @@ impl<'tcx> InferCtxt<'tcx> {
// and post-borrowck analysis mode. We may need to modify its uses
// to support PostBorrowckAnalysis in the old solver as well.
TypingMode::Coherence
| TypingMode::Reflection
| TypingMode::PostBorrowckAnalysis { .. }
| TypingMode::PostAnalysis => false,
}
Expand Down Expand Up @@ -1371,6 +1372,7 @@ impl<'tcx> InferCtxt<'tcx> {
}
mode @ (ty::TypingMode::Coherence
| ty::TypingMode::PostBorrowckAnalysis { .. }
| ty::TypingMode::Reflection
| ty::TypingMode::PostAnalysis) => mode,
};
ty::TypingEnv { typing_mode, param_env }
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_infer/src/infer/opaque_types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,9 @@ impl<'tcx> InferCtxt<'tcx> {
.map(|obligation| obligation.as_goal()),
);
}
mode @ (ty::TypingMode::PostBorrowckAnalysis { .. } | ty::TypingMode::PostAnalysis) => {
mode @ (ty::TypingMode::PostBorrowckAnalysis { .. }
| ty::TypingMode::PostAnalysis
| ty::TypingMode::Reflection) => {
bug!("insert hidden type in {mode:?}")
}
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ provide! { tcx, def_id, other, cdata,
fn_sig => { table }
codegen_fn_attrs => { table }
impl_trait_header => { table }
impl_is_fully_generic_for_reflection => { table_direct }
const_param_default => { table }
object_lifetime_default => { table }
thir_abstract_const => { table }
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_metadata/src/rmeta/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2173,6 +2173,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
let header = tcx.impl_trait_header(def_id);
record!(self.tables.impl_trait_header[def_id] <- header);

let impl_is_fully_generic_for_reflection =
tcx.impl_is_fully_generic_for_reflection(def_id);
self.tables
.impl_is_fully_generic_for_reflection
.set(def_id.index, impl_is_fully_generic_for_reflection);

self.tables.defaultness.set(def_id.index, tcx.defaultness(def_id));

let trait_ref = header.trait_ref.instantiate_identity();
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_metadata/src/rmeta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ define_tables! {
constness: Table<DefIndex, hir::Constness>,
safety: Table<DefIndex, hir::Safety>,
defaultness: Table<DefIndex, hir::Defaultness>,
impl_is_fully_generic_for_reflection: Table<DefIndex, bool>,

- optional:
attributes: Table<DefIndex, LazyArray<hir::Attribute>>,
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_middle/src/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1126,6 +1126,15 @@ rustc_queries! {
separate_provide_extern
}

/// Whether all generic parameters of the type are unique unconstrained generic parameters
/// of the impl. `Bar<'static>` or `Foo<'a, 'a>` or outlives bounds on the lifetimes cause
/// this boolean to be false and `try_as_dyn` to return `None`.
query impl_is_fully_generic_for_reflection(impl_id: DefId) -> bool {
desc { |tcx| "computing trait implemented by `{}`", tcx.def_path_str(impl_id) }
cache_on_disk_if { impl_id.is_local() }
separate_provide_extern
}

/// Given an `impl_def_id`, return true if the self type is guaranteed to be unsized due
/// to either being one of the built-in unsized types (str/slice/dyn) or to be a struct
/// whose tail is one of those types.
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_middle/src/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ pub enum SelectionCandidate<'tcx> {
BuiltinUnsizeCandidate,

BikeshedGuaranteedNoDropCandidate,

TryAsDynCandidate,
}

/// The result of trait evaluation. The order is important
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_middle/src/ty/context/impl_interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
self.impl_polarity(impl_def_id)
}

fn is_fully_generic_for_reflection(self, impl_def_id: Self::ImplId) -> bool {
self.impl_is_fully_generic_for_reflection(impl_def_id)
}

fn trait_is_auto(self, trait_def_id: DefId) -> bool {
self.trait_is_auto(trait_def_id)
}
Expand Down Expand Up @@ -799,6 +803,7 @@ bidirectional_lang_item_map! {
Sized,
TransmuteTrait,
TrivialClone,
TryAsDyn,
Tuple,
Unpin,
Unsize,
Expand Down
69 changes: 67 additions & 2 deletions compiler/rustc_middle/src/ty/generics.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use std::ops::ControlFlow;

use rustc_ast as ast;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def_id::DefId;
use rustc_macros::{HashStable, TyDecodable, TyEncodable};
use rustc_span::{Span, Symbol, kw};
use rustc_type_ir::{TypeSuperVisitable as _, TypeVisitable, TypeVisitor};
use tracing::instrument;

use super::{Clause, InstantiatedPredicates, ParamConst, ParamTy, Ty, TyCtxt};
use crate::ty;
use crate::ty::{EarlyBinder, GenericArgsRef};
use crate::ty::{self, ClauseKind, EarlyBinder, GenericArgsRef, Region, RegionKind, TyKind};

#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)]
pub enum GenericParamDefKind {
Expand Down Expand Up @@ -421,6 +423,69 @@ impl<'tcx> GenericPredicates<'tcx> {
instantiated.predicates.extend(self.predicates.iter().map(|(p, _)| p));
instantiated.spans.extend(self.predicates.iter().map(|(_, s)| s));
}

/// Allow simple where bounds like `T: Debug`, but prevent any kind of
/// outlives bounds or uses of generic parameters on the right hand side.
pub fn is_fully_generic_for_reflection(self) -> bool {
struct ParamChecker;
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ParamChecker {
type Result = ControlFlow<()>;
fn visit_region(&mut self, r: Region<'tcx>) -> Self::Result {
match r.kind() {
RegionKind::ReEarlyParam(_)
| RegionKind::ReStatic
| RegionKind::ReVar(_)
| RegionKind::RePlaceholder(_)
| RegionKind::ReErased
| RegionKind::ReError(_) => ControlFlow::Break(()),
RegionKind::ReBound(..) | RegionKind::ReLateParam(_) => {
ControlFlow::Continue(())
}
}
}

fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
match t.kind() {
TyKind::Param(_p) => {
// Reject using parameters used in the type in where bounds
return ControlFlow::Break(());
}
TyKind::Alias(..) => return ControlFlow::Break(()),
_ => (),
}
t.super_visit_with(self)
}
}

// Pessimistic: if any of the parameters have where bounds
// don't allow this impl to be used.
self.predicates.iter().all(|(clause, _)| {
match clause.kind().skip_binder() {
ClauseKind::Trait(trait_predicate) => {
// In a `T: Trait`, if the rhs bound does not contain any generic params
// or 'static lifetimes, then it cannot transitively cause such requirements,
// considering we apply the fully-generic-for-reflection rules to any impls for
// that trait, too.
if matches!(trait_predicate.self_ty().kind(), ty::Param(_))
&& trait_predicate.trait_ref.args[1..]
.iter()
.all(|arg| arg.visit_with(&mut ParamChecker).is_continue())
{
return true;
}
}
ClauseKind::RegionOutlives(_)
| ClauseKind::TypeOutlives(_)
| ClauseKind::Projection(_)
| ClauseKind::ConstArgHasType(_, _)
| ClauseKind::WellFormed(_)
| ClauseKind::ConstEvaluatable(_)
| ClauseKind::HostEffect(_)
| ClauseKind::UnstableFeature(_) => {}
}
clause.visit_with(&mut ParamChecker).is_continue()
})
}
}

/// `[const]` bounds for a given item. This is represented using a struct much like
Expand Down
52 changes: 52 additions & 0 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use std::fmt::Debug;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use std::num::NonZero;
use std::ops::ControlFlow;
use std::ptr::NonNull;
use std::{fmt, iter, str};

Expand Down Expand Up @@ -278,6 +279,57 @@ pub struct ImplTraitHeader<'tcx> {
pub constness: hir::Constness,
}

impl<'tcx> ImplTraitHeader<'tcx> {
/// For trait impls, checks whether the type and trait only have generic lifetime parameters in their
/// arguments and only use any generic param once.
/// This is a pessimistic analysis, so it will reject projection types (except for weak aliases)
/// and other types that may be actually ok. We can allow more in the future.
pub fn is_fully_generic_for_reflection(self) -> bool {
#[derive(Default)]
struct ParamFinder {
seen: FxHashSet<u32>,
}

impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ParamFinder {
type Result = ControlFlow<()>;
fn visit_region(&mut self, r: Region<'tcx>) -> Self::Result {
match r.kind() {
RegionKind::ReEarlyParam(param) => {
if self.seen.insert(param.index) {
ControlFlow::Continue(())
} else {
ControlFlow::Break(())
}
}
RegionKind::ReBound(..) | RegionKind::ReLateParam(_) => {
ControlFlow::Continue(())
}
RegionKind::ReStatic
| RegionKind::ReVar(_)
| RegionKind::RePlaceholder(_)
| RegionKind::ReErased
| RegionKind::ReError(_) => ControlFlow::Break(()),
}
}

fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
match t.kind() {
TyKind::Param(p) => {
// Reject using a parameter twice (e.g. in `Foo<T, T>`)
if !self.seen.insert(p.index) {
return ControlFlow::Break(());
}
}
TyKind::Alias(..) => return ControlFlow::Break(()),
_ => (),
}
t.super_visit_with(self)
}
}
self.trait_ref.instantiate_identity().visit_with(&mut ParamFinder::default()).is_continue()
}
}

#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable, Debug)]
#[derive(TypeFoldable, TypeVisitable, Default)]
pub enum Asyncness {
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_middle/src/ty/predicate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,12 @@ impl<'tcx> UpcastFrom<TyCtxt<'tcx>, TypeOutlivesPredicate<'tcx>> for Predicate<'
}
}

impl<'tcx> UpcastFrom<TyCtxt<'tcx>, PolyTypeOutlivesPredicate<'tcx>> for Predicate<'tcx> {
fn upcast_from(from: PolyTypeOutlivesPredicate<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
from.map_bound(|p| PredicateKind::Clause(ClauseKind::TypeOutlives(p))).upcast(tcx)
}
}

impl<'tcx> UpcastFrom<TyCtxt<'tcx>, ProjectionPredicate<'tcx>> for Predicate<'tcx> {
fn upcast_from(from: ProjectionPredicate<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
ty::Binder::dummy(PredicateKind::Clause(ClauseKind::Projection(from))).upcast(tcx)
Expand Down
18 changes: 18 additions & 0 deletions compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,11 @@ where
goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution>;

fn consider_builtin_try_as_dyn_candidate(
ecx: &mut EvalCtxt<'_, D>,
goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution>;

/// Consider (possibly several) candidates to upcast or unsize a type to another
/// type, excluding the coercion of a sized type into a `dyn Trait`.
///
Expand Down Expand Up @@ -525,6 +530,10 @@ where
let cx = self.cx();
let trait_def_id = goal.predicate.trait_def_id(cx);

if let TypingMode::Reflection = self.typing_mode() {
return;
}

// N.B. When assembling built-in candidates for lang items that are also
// `auto` traits, then the auto trait candidate that is assembled in
// `consider_auto_trait_candidate` MUST be disqualified to remain sound.
Expand Down Expand Up @@ -617,6 +626,9 @@ where
Some(SolverTraitLangItem::BikeshedGuaranteedNoDrop) => {
G::consider_builtin_bikeshed_guaranteed_no_drop_candidate(self, goal)
}
Some(SolverTraitLangItem::TryAsDyn) => {
G::consider_builtin_try_as_dyn_candidate(self, goal)
}
_ => Err(NoSolution),
}
};
Expand Down Expand Up @@ -798,6 +810,10 @@ where
return;
}

if let TypingMode::Reflection = self.typing_mode() {
return;
}

let self_ty = goal.predicate.self_ty();
let bounds = match self_ty.kind() {
ty::Bool
Expand Down Expand Up @@ -930,6 +946,7 @@ where
TypingMode::Coherence => return,
TypingMode::Analysis { .. }
| TypingMode::Borrowck { .. }
| TypingMode::Reflection
| TypingMode::PostBorrowckAnalysis { .. }
| TypingMode::PostAnalysis => {}
}
Expand Down Expand Up @@ -993,6 +1010,7 @@ where
TypingMode::Analysis { .. } => self.opaques_with_sub_unified_hidden_type(self_ty),
TypingMode::Coherence
| TypingMode::Borrowck { .. }
| TypingMode::Reflection
| TypingMode::PostBorrowckAnalysis { .. }
| TypingMode::PostAnalysis => vec![],
};
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ where
TypingMode::Coherence => Certainty::AMBIGUOUS,
TypingMode::Analysis { .. }
| TypingMode::Borrowck { .. }
| TypingMode::Reflection
| TypingMode::PostBorrowckAnalysis { .. }
| TypingMode::PostAnalysis => return Err(NoSolution),
},
Expand Down Expand Up @@ -424,6 +425,13 @@ where
unreachable!("BikeshedGuaranteedNoDrop is not const");
}

fn consider_builtin_try_as_dyn_candidate(
_ecx: &mut EvalCtxt<'_, D>,
goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("`TryAsDynCompat` is not const: {:?}", goal)
}

fn consider_structural_builtin_unsize_candidates(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_next_trait_solver/src/solve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ where
fn opaque_type_is_rigid(&self, def_id: I::DefId) -> bool {
match self.typing_mode() {
// Opaques are never rigid outside of analysis mode.
TypingMode::Coherence | TypingMode::PostAnalysis => false,
TypingMode::Reflection | TypingMode::Coherence | TypingMode::PostAnalysis => false,
// During analysis, opaques are rigid unless they may be defined by
// the current body.
TypingMode::Analysis { defining_opaque_types_and_generators: non_rigid_opaques }
Expand Down
Loading
Loading