-
Notifications
You must be signed in to change notification settings - Fork 13.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Solution to trait alias bug #65673 #66813
Changes from all commits
7c4ad1f
3573acc
fb35f14
90e9d26
b2f4eed
0f84812
34080fb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,6 @@ | |
//! The main routine here is `ast_ty_to_ty()`; each use is parameterized by an | ||
//! instance of `AstConv`. | ||
|
||
use errors::{Applicability, DiagnosticId}; | ||
use crate::hir::{self, GenericArg, GenericArgs, ExprKind}; | ||
use crate::hir::def::{CtorOf, Res, DefKind}; | ||
use crate::hir::def_id::DefId; | ||
|
@@ -12,32 +11,34 @@ use crate::lint; | |
use crate::middle::lang_items::SizedTraitLangItem; | ||
use crate::middle::resolve_lifetime as rl; | ||
use crate::namespace::Namespace; | ||
use crate::require_c_abi_if_c_variadic; | ||
use crate::util::common::ErrorReported; | ||
use crate::util::nodemap::FxHashMap; | ||
|
||
use rustc::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS; | ||
use rustc::traits; | ||
use rustc::ty::{self, DefIdTree, Ty, TyCtxt, Const, ToPredicate, TypeFoldable}; | ||
use rustc::ty::{GenericParamDef, GenericParamDefKind}; | ||
use rustc::ty::subst::{self, Subst, InternalSubsts, SubstsRef}; | ||
use rustc::ty::wf::object_region_bounds; | ||
use rustc_target::spec::abi; | ||
use crate::require_c_abi_if_c_variadic; | ||
use smallvec::SmallVec; | ||
|
||
use syntax::ast; | ||
use syntax::errors::pluralize; | ||
use syntax::feature_gate::{GateIssue, emit_feature_err}; | ||
use syntax::util::lev_distance::find_best_match_for_name; | ||
use syntax::symbol::sym; | ||
use syntax_pos::{DUMMY_SP, Span, MultiSpan}; | ||
use crate::util::common::ErrorReported; | ||
use crate::util::nodemap::FxHashMap; | ||
|
||
use errors::{Applicability, DiagnosticId}; | ||
use rustc_data_structures::fx::FxHashSet; | ||
use rustc_error_codes::*; | ||
use smallvec::SmallVec; | ||
|
||
use std::collections::BTreeSet; | ||
use std::iter; | ||
use std::slice; | ||
|
||
use rustc_data_structures::fx::FxHashSet; | ||
|
||
use rustc_error_codes::*; | ||
|
||
#[derive(Debug)] | ||
pub struct PathSeg(pub DefId, pub usize); | ||
alexreg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
|
@@ -64,8 +65,7 @@ pub trait AstConv<'tcx> { | |
&self, | ||
param: Option<&ty::GenericParamDef>, | ||
span: Span, | ||
) | ||
-> Option<ty::Region<'tcx>>; | ||
) -> Option<ty::Region<'tcx>>; | ||
|
||
/// Returns the type to use when a type is omitted. | ||
fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx>; | ||
|
@@ -1247,8 +1247,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { | |
// Expand trait aliases recursively and check that only one regular (non-auto) trait | ||
// is used and no 'maybe' bounds are used. | ||
let expanded_traits = | ||
traits::expand_trait_aliases(tcx, bounds.trait_bounds.iter().cloned()); | ||
let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) = | ||
traits::expand_trait_aliases(tcx, bounds.trait_bounds.iter().cloned()) | ||
// Ensure that trait ref is to self type and not some type param. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems pretty subtle and not obviously correct. One thing I noticed is that the original ICE was based on something like However, something like this would work ok: trait Foo = where Self: Display; which I think would be equivalent to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm a bit worried here about an example like this: #![feature(trait_alias)]
trait Foo<T> = where T: PartialEq<Self>;
fn main() {
let _: &dyn Foo<()> = &();
} When I test this, I currently get some wacky ICE =) But it seems like we would screen out the My intuition is that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The key thing is to avoid trying to create existential predicates for predicates where the self ty is not the dummy self ty, which is of course impossible. This filter might be blocking out too much (projections on The relevant test is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As for object safety, I guess we need to modify that code explicitly? Given such predicates on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't quite understand your response here, @alexreg. What is the behavior on the example I gave, for example? I don't think that trait Alias<T> = Any where T: Trait; whereas the trait alias in my example was Also, regarding the difference between There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @nikomatsakis Okay, I've gotten your expected behaviour implemented. (And I managed to remove the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know! I think I'm inclined to preserve the status quo, whatever it is, though if/when we generalize to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @nikomatsakis Okay, sounds fair. I'll push shortly and this PR will be ready for review. |
||
.filter(|info| info.trait_ref().self_ty() == dummy_self); | ||
let (auto_traits, regular_traits): (Vec<_>, Vec<_>) = | ||
expanded_traits.partition(|i| tcx.trait_is_auto(i.trait_ref().def_id())); | ||
if regular_traits.len() > 1 { | ||
let first_trait = ®ular_traits[0]; | ||
|
@@ -1289,7 +1291,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { | |
let mut associated_types = BTreeSet::default(); | ||
|
||
let regular_traits_refs = bounds.trait_bounds | ||
.into_iter() | ||
.iter() | ||
.cloned() | ||
.filter(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id())) | ||
.map(|(trait_ref, _)| trait_ref); | ||
for trait_ref in traits::elaborate_trait_refs(tcx, regular_traits_refs) { | ||
|
@@ -1398,50 +1401,53 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { | |
err.emit(); | ||
} | ||
|
||
let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, I don't love the way these variables shadow the variables above. In general, this function seems to be awfully big, and these shadows are quite far apart. I had to expand the diff fully just to be sure that shadowing was even happening. I think we should extract a helper function for the logic above, perhaps, and then a separate helper for the logic below. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, this fn was already very big, and that’s just gotten worse now. I’ll see what I can do. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, |
||
bounds.trait_bounds | ||
.into_iter() | ||
.map(|(trait_ref, _)| trait_ref) | ||
.partition(|i| tcx.trait_is_auto(i.def_id())); | ||
|
||
// De-duplicate auto traits so that, e.g., `dyn Trait + Send + Send` is the same as | ||
// `dyn Trait + Send`. | ||
auto_traits.sort_by_key(|i| i.trait_ref().def_id()); | ||
auto_traits.dedup_by_key(|i| i.trait_ref().def_id()); | ||
debug!("regular_traits: {:?}", regular_traits); | ||
debug!("auto_traits: {:?}", auto_traits); | ||
auto_traits.sort_by_key(|i| i.def_id()); | ||
auto_traits.dedup_by_key(|i| i.def_id()); | ||
debug!( | ||
"conv_object_ty_poly_trait_ref: regular_traits={:?} auto_traits={:?}", | ||
regular_traits, auto_traits | ||
); | ||
|
||
// Transform a `PolyTraitRef` into a `PolyExistentialTraitRef` by | ||
// removing the dummy `Self` type (`trait_object_dummy_self`). | ||
let trait_ref_to_existential = |trait_ref: ty::TraitRef<'tcx>| { | ||
if trait_ref.self_ty() != dummy_self { | ||
// FIXME: There appears to be a missing filter on top of `expand_trait_aliases`, | ||
// which picks up non-supertraits where clauses - but also, the object safety | ||
// completely ignores trait aliases, which could be object safety hazards. We | ||
// `delay_span_bug` here to avoid an ICE in stable even when the feature is | ||
// disabled. (#66420) | ||
tcx.sess.delay_span_bug(DUMMY_SP, &format!( | ||
"trait_ref_to_existential called on {:?} with non-dummy Self", | ||
trait_ref, | ||
)); | ||
bug!("trait_ref_to_existential called on {:?} with non-dummy Self", trait_ref); | ||
} | ||
ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) | ||
}; | ||
|
||
// Erase the `dummy_self` (`trait_object_dummy_self`) used above. | ||
let existential_trait_refs = regular_traits.iter().map(|i| { | ||
i.trait_ref().map_bound(|trait_ref| trait_ref_to_existential(trait_ref)) | ||
}); | ||
let existential_projections = bounds.projection_bounds.iter().map(|(bound, _)| { | ||
bound.map_bound(|b| { | ||
let trait_ref = trait_ref_to_existential(b.projection_ty.trait_ref(tcx)); | ||
ty::ExistentialProjection { | ||
ty: b.ty, | ||
item_def_id: b.projection_ty.item_def_id, | ||
substs: trait_ref.substs, | ||
} | ||
}) | ||
}); | ||
let existential_trait_refs = regular_traits | ||
.iter() | ||
.map(|i| i.map_bound(|trait_ref| trait_ref_to_existential(trait_ref))); | ||
let existential_projections = bounds.projection_bounds | ||
.iter() | ||
.map(|(bound, _)| { | ||
bound.map_bound(|b| { | ||
let trait_ref = trait_ref_to_existential(b.projection_ty.trait_ref(tcx)); | ||
ty::ExistentialProjection { | ||
ty: b.ty, | ||
item_def_id: b.projection_ty.item_def_id, | ||
substs: trait_ref.substs, | ||
} | ||
}) | ||
}); | ||
|
||
// Calling `skip_binder` is okay because the predicates are re-bound. | ||
let regular_trait_predicates = existential_trait_refs.map( | ||
|trait_ref| ty::ExistentialPredicate::Trait(*trait_ref.skip_binder())); | ||
let auto_trait_predicates = auto_traits.into_iter().map( | ||
|trait_ref| ty::ExistentialPredicate::AutoTrait(trait_ref.trait_ref().def_id())); | ||
let regular_trait_predicates = existential_trait_refs | ||
.map(|trait_ref| ty::ExistentialPredicate::Trait(*trait_ref.skip_binder())); | ||
let auto_trait_predicates = auto_traits | ||
.into_iter() | ||
.map(|trait_ref| ty::ExistentialPredicate::AutoTrait(trait_ref.def_id())); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To confirm, also no functional changes here, correct? ( I didn't spot any ) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct, just made it more readable since I was editing this fn anyway. |
||
let mut v = | ||
regular_trait_predicates | ||
.chain(auto_trait_predicates) | ||
|
@@ -1469,10 +1475,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { | |
} | ||
}) | ||
}; | ||
debug!("region_bound: {:?}", region_bound); | ||
debug!("conv_object_ty_poly_trait_ref: region_bound={:?}", region_bound); | ||
|
||
let ty = tcx.mk_dynamic(existential_predicates, region_bound); | ||
debug!("trait_object_type: {:?}", ty); | ||
debug!("conv_object_ty_poly_trait_ref: trait_object_type={:?}", ty); | ||
ty | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,12 @@ | ||
// ignore-tidy-linelength | ||
|
||
trait Trait {} | ||
|
||
pub fn main() { | ||
let x: Vec<dyn Trait + Sized> = Vec::new(); | ||
//~^ ERROR only auto traits can be used as additional traits in a trait object | ||
//~| ERROR the size for values of type | ||
//~| ERROR the size for values of type | ||
//~^ ERROR only auto traits can be used as additional traits in a trait object [E0225] | ||
//~| ERROR the size for values of type `dyn std::marker::Sized` cannot be known at compilation time [E0277] | ||
//~| the trait `std::marker::Sized` cannot be made into an object [E0038] | ||
//~| ERROR the size for values of type `dyn std::marker::Sized` cannot be known at compilation time [E0277] | ||
//~| the trait `std::marker::Sized` cannot be made into an object [E0038] | ||
} |
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be clear, I believe that there are no functional changes to this file? (Just checking)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I can remove this change if you like. I think I just forgot. Makes sense to inline this, but it's not important.