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

// 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
7 changes: 7 additions & 0 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,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 @@ -1266,6 +1267,12 @@ pub fn suggest_impl_trait<'tcx>(
None
}

fn impl_is_fully_generic_for_reflection(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
tcx.type_of(def_id).instantiate_identity().is_fully_generic_for_reflection().is_ok_and(
|params| tcx.explicit_predicates_of(def_id).is_fully_generic_for_reflection(params),
)
}

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 @@ -236,6 +236,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 @@ -2167,6 +2167,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/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1127,6 +1127,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 @@ -180,6 +180,8 @@ pub enum SelectionCandidate<'tcx> {
BuiltinUnsizeCandidate,

BikeshedGuaranteedNoDropCandidate,

TryAsDynCandidate,
}

/// The result of trait evaluation. The order is important
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/ty/assoc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,10 @@ impl AssocItems {
self.items.iter().map(|(_, v)| v)
}

pub fn all(&self, f: impl Fn(&ty::AssocItem) -> bool) -> bool {
self.in_definition_order().all(f)
}

pub fn len(&self) -> usize {
self.items.len()
}
Expand Down
25 changes: 25 additions & 0 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,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 All @@ -714,6 +718,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
self.trait_def(trait_def_id).implement_via_object
}

fn trait_is_try_as_dyn_compatible(self, def_id: DefId) -> bool {
self.trait_is_try_as_dyn_compatible(def_id)
}

fn trait_is_unsafe(self, trait_def_id: Self::DefId) -> bool {
self.trait_def(trait_def_id).safety.is_unsafe()
}
Expand Down Expand Up @@ -858,6 +866,7 @@ bidirectional_lang_item_map! {
Sized,
TransmuteTrait,
TrivialClone,
TryAsDyn,
Tuple,
Unpin,
Unsize,
Expand Down Expand Up @@ -2382,6 +2391,22 @@ impl<'tcx> TyCtxt<'tcx> {
None
}

pub fn trait_is_try_as_dyn_compatible(self, def_id: DefId) -> bool {
self.associated_items(def_id).all(|method| {
if let ty::AssocKind::Fn { .. } = method.kind {
// Methods with `Self: Sized` are not accessible in `dyn Trait`, so
// we do not need to care about them.
if !self.generics_require_sized_self(method.def_id) {
// FIXME(try_as_dyn): can probably allow other bounds
if !self.explicit_predicates_of(method.def_id).predicates.is_empty() {
return false;
}
}
}
true
})
}

/// Determines whether identifiers in the assembly have strict naming rules.
/// Currently, only NVPTX* targets need it.
pub fn has_strict_asm_symbol_naming(self) -> bool {
Expand Down
56 changes: 53 additions & 3 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_data_structures::fx::{FxHashMap, FxHashSet};
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, EarlyBinder, GenericArgsRef, Region, RegionKind, TyKind};

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

pub fn is_fully_generic_for_reflection(self, params: FxHashSet<u32>) -> bool {
#[derive(Default)]
struct ParamChecker {
params: FxHashSet<u32>,
}
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(param) => {
if self.params.contains(&param.index) {
ControlFlow::Break(())
} else {
ControlFlow::Continue(())
}
}
RegionKind::ReBound(..)
| RegionKind::ReLateParam(_)
| RegionKind::ReStatic
| RegionKind::ReVar(_)
| RegionKind::RePlaceholder(_)
| RegionKind::ReErased
| RegionKind::ReError(_) => 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
if self.params.contains(&p.index) {
return ControlFlow::Break(());
}
}
TyKind::Alias(..) => return ControlFlow::Break(()),
_ => (),
}
t.super_visit_with(self)
}
}

let mut checker = ParamChecker { params };

// Pessimistic: if any of the lifetimes used in the type show up in where bounds
// don't allow this impl to be used.
self.predicates.iter().all(|(clause, _)| clause.visit_with(&mut checker).is_continue())
}
}

/// `[const]` bounds for a given item. This is represented using a struct much like
Expand Down
55 changes: 54 additions & 1 deletion compiler/rustc_middle/src/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use std::ops::{ControlFlow, Range};

use hir::def::{CtorKind, DefKind};
use rustc_abi::{FIRST_VARIANT, FieldIdx, ScalableElt, VariantIdx};
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{ErrorGuaranteed, MultiSpan};
use rustc_hir as hir;
use rustc_hir::LangItem;
Expand All @@ -27,7 +28,8 @@ use crate::traits::ObligationCause;
use crate::ty::InferTy::*;
use crate::ty::{
self, AdtDef, BoundRegionKind, Discr, GenericArg, GenericArgs, GenericArgsRef, List, ParamEnv,
Region, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy,
Region, RegionKind, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor,
UintTy,
};

// Re-export and re-parameterize some `I = TyCtxt<'tcx>` types here
Expand Down Expand Up @@ -1365,6 +1367,57 @@ impl<'tcx> Ty<'tcx> {
}
}

/// For self-types of impls, checks whether the type only has generic parameters in its
/// arguments and only uses the generic params once, too.
/// Pessimistic analysis, so it will reject projection types (except for weak aliases)
/// and other types that may be ok.
/// Returns the list of used params if successful.
pub fn is_fully_generic_for_reflection(self) -> Result<FxHashSet<u32>, ()> {
#[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)
}
}
let mut finder = ParamFinder::default();
if finder.visit_ty(self).is_continue() { Ok(finder.seen) } else { Err(()) }
}

pub fn boxed_ty(self) -> Option<Ty<'tcx>> {
match self.kind() {
Adt(def, args) if def.is_box() => Some(args.type_at(0)),
Expand Down
10 changes: 10 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 @@ -617,6 +622,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 @@ -928,6 +936,7 @@ where
TypingMode::Coherence => return,
TypingMode::Analysis { .. }
| TypingMode::Borrowck { .. }
| TypingMode::Reflection
| TypingMode::PostBorrowckAnalysis { .. }
| TypingMode::PostAnalysis => {}
}
Expand Down Expand Up @@ -991,6 +1000,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
Loading
Loading