/// Depending on the stage of compilation, we want projection to be
/// more or less conservative.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
-pub enum ProjectionMode {
+pub enum Reveal {
/// FIXME (#32205)
/// At coherence-checking time, we're still constructing the
/// specialization graph, and thus we only project
///
/// The projection would succeed if `Output` had been defined
/// directly in the impl for `u8`.
- Topmost,
+ ExactMatch,
/// At type-checking time, we refuse to project any associated
/// type that is marked `default`. Non-`default` ("final") types
/// fn main() {
/// let <() as Assoc>::Output = true;
/// }
- AnyFinal,
+ NotSpecializable,
- /// At trans time, all projections will succeed.
- Any,
-}
-
-impl ProjectionMode {
- pub fn is_topmost(&self) -> bool {
- match *self {
- ProjectionMode::Topmost => true,
- _ => false,
- }
- }
-
- pub fn is_any_final(&self) -> bool {
- match *self {
- ProjectionMode::AnyFinal => true,
- _ => false,
- }
- }
-
- pub fn is_any(&self) -> bool {
- match *self {
- ProjectionMode::Any => true,
- _ => false,
- }
- }
+ /// At trans time, all monomorphic projections will succeed.
+ /// Also, `impl Trait` is normalized to the concrete type,
+ /// which has to be already collected by type-checking.
+ ///
+ /// NOTE: As `impl Trait`'s concrete type should *never*
+ /// be observable directly by the user, `Reveal::All`
+ /// should not be used by checks which may expose
+ /// type equality or type contents to the user.
+ /// There are some exceptions, e.g. around OIBITS and
+ /// transmute-checking, which expose some details, but
+ /// not the whole concrete type of the `impl Trait`.
+ All,
}
-
pub type PolyProjectionObligation<'tcx> =
Obligation<'tcx, ty::PolyProjectionPredicate<'tcx>>;
let ty = ty.super_fold_with(self);
match ty.sty {
+ ty::TyAnon(def_id, substs) if !substs.has_escaping_regions() => { // (*)
+ // Only normalize `impl Trait` after type-checking, usually in trans.
+ if self.selcx.projection_mode() == Reveal::All {
+ let generic_ty = self.tcx().lookup_item_type(def_id).ty;
+ let concrete_ty = generic_ty.subst(self.tcx(), substs);
+ self.fold_ty(concrete_ty)
+ } else {
+ ty
+ }
+ }
+
ty::TyProjection(ref data) if !data.has_escaping_regions() => { // (*)
// (*) This is kind of hacky -- we need to be able to
debug!("assemble_candidates_from_trait_def(..)");
// Check whether the self-type is itself a projection.
- let trait_ref = match obligation_trait_ref.self_ty().sty {
- ty::TyProjection(ref data) => data.trait_ref.clone(),
+ let (def_id, substs) = match obligation_trait_ref.self_ty().sty {
+ ty::TyProjection(ref data) => {
+ (data.trait_ref.def_id, data.trait_ref.substs)
+ }
+ ty::TyAnon(def_id, substs) => (def_id, substs),
ty::TyInfer(ty::TyVar(_)) => {
// If the self-type is an inference variable, then it MAY wind up
// being a projected type, so induce an ambiguity.
};
// If so, extract what we know from the trait and try to come up with a good answer.
- let trait_predicates = selcx.tcx().lookup_predicates(trait_ref.def_id);
- let bounds = trait_predicates.instantiate(selcx.tcx(), trait_ref.substs);
+ let trait_predicates = selcx.tcx().lookup_predicates(def_id);
+ let bounds = trait_predicates.instantiate(selcx.tcx(), substs);
let bounds = elaborate_predicates(selcx.tcx(), bounds.predicates.into_vec());
assemble_candidates_from_predicates(selcx,
obligation,
candidate_set.vec.push(ProjectionTyCandidate::Select);
}
- super::VtableImpl(ref impl_data) if !selcx.projection_mode().is_any() => {
+ super::VtableImpl(ref impl_data) => {
// We have to be careful when projecting out of an
// impl because of specialization. If we are not in
// trans (i.e., projection mode is not "any"), and the
impl_data.impl_def_id,
obligation.predicate.item_name);
let new_candidate = if let Some(node_item) = opt_node_item {
- if node_item.node.is_from_trait() {
- if node_item.item.ty.is_some() {
- // The impl inherited a `type Foo =
- // Bar` given in the trait, which is
- // implicitly default. No candidate.
- None
- } else {
- // The impl did not specify `type` and neither
- // did the trait:
- //
- // ```rust
- // trait Foo { type T; }
- // impl Foo for Bar { }
- // ```
- //
- // This is an error, but it will be
- // reported in `check_impl_items_against_trait`.
- // We accept it here but will flag it as
- // an error when we confirm the candidate
- // (which will ultimately lead to `normalize_to_error`
- // being invoked).
+ let is_default = if node_item.node.is_from_trait() {
+ // If true, the impl inherited a `type Foo = Bar`
+ // given in the trait, which is implicitly default.
+ // Otherwise, the impl did not specify `type` and
+ // neither did the trait:
+ //
+ // ```rust
+ // trait Foo { type T; }
+ // impl Foo for Bar { }
+ // ```
+ //
+ // This is an error, but it will be
+ // reported in `check_impl_items_against_trait`.
+ // We accept it here but will flag it as
+ // an error when we confirm the candidate
+ // (which will ultimately lead to `normalize_to_error`
+ // being invoked).
+ node_item.item.ty.is_some()
+ } else {
+ node_item.item.defaultness.is_default()
+ };
+
+ // Only reveal a specializable default if we're past type-checking
+ // and the obligations is monomorphic, otherwise passes such as
+ // transmute checking and polymorphic MIR optimizations could
+ // get a result which isn't correct for all monomorphizations.
+ if !is_default {
+ Some(ProjectionTyCandidate::Select)
+ } else if selcx.projection_mode() == Reveal::All {
+ assert!(!poly_trait_ref.needs_infer());
+ if !poly_trait_ref.needs_subst() {
Some(ProjectionTyCandidate::Select)
+ } else {
+ None
}
- } else if node_item.item.defaultness.is_default() {
- // The impl specified `default type Foo =
- // Bar`. No candidate.
- None
} else {
- // The impl specified `type Foo = Bar`
- // with no default. Add a candidate.
- Some(ProjectionTyCandidate::Select)
+ None
}
} else {
// This is saying that neither the trait nor
};
candidate_set.vec.extend(new_candidate);
}
- super::VtableImpl(_) => {
- // In trans mode, we can just project out of impls, no prob.
- assert!(selcx.projection_mode().is_any());
- candidate_set.vec.push(ProjectionTyCandidate::Select);
- }
super::VtableParam(..) => {
// This case tell us nothing about the value of an
// associated type. Consider:
/// starting from the given impl.
///
/// Based on the "projection mode", this lookup may in fact only examine the
-/// topmost impl. See the comments for `ProjectionMode` for more details.
+/// topmost impl. See the comments for `Reveal` for more details.
fn assoc_ty_def<'cx, 'gcx, 'tcx>(
selcx: &SelectionContext<'cx, 'gcx, 'tcx>,
impl_def_id: DefId,
{
let trait_def_id = selcx.tcx().impl_trait_ref(impl_def_id).unwrap().def_id;
- if selcx.projection_mode().is_topmost() {
+ if selcx.projection_mode() == Reveal::ExactMatch {
let impl_node = specialization_graph::Node::Impl(impl_def_id);
for item in impl_node.items(selcx.tcx()) {
if let ty::TypeTraitItem(assoc_ty) = item {