Skip to content
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

Replace associated item bound vars with placeholders when projecting #86993

Merged
merged 4 commits into from
Jul 16, 2021
Merged
Show file tree
Hide file tree
Changes from 3 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
3 changes: 2 additions & 1 deletion compiler/rustc_middle/src/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2478,9 +2478,10 @@ impl<'tcx> ty::Instance<'tcx> {
// `src/test/ui/polymorphization/normalized_sig_types.rs`), and codegen not keeping
// track of a polymorphization `ParamEnv` to allow normalizing later.
let mut sig = match *ty.kind() {
ty::FnDef(def_id, substs) => tcx
ty::FnDef(def_id, substs) if tcx.sess.opts.debugging_opts.polymorphize => tcx
.normalize_erasing_regions(tcx.param_env(def_id), tcx.fn_sig(def_id))
.subst(tcx, substs),
ty::FnDef(def_id, substs) => tcx.fn_sig(def_id).subst(tcx, substs),
_ => unreachable!(),
};

Expand Down
370 changes: 369 additions & 1 deletion compiler/rustc_trait_selection/src/traits/project.rs

Large diffs are not rendered by default.

86 changes: 86 additions & 0 deletions compiler/rustc_trait_selection/src/traits/query/normalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> {
error: false,
cache: SsoHashMap::new(),
anon_depth: 0,
universes: vec![],
};

let result = value.fold_with(&mut normalizer);
Expand Down Expand Up @@ -91,13 +92,24 @@ struct QueryNormalizer<'cx, 'tcx> {
cache: SsoHashMap<Ty<'tcx>, Ty<'tcx>>,
error: bool,
anon_depth: usize,
universes: Vec<Option<ty::UniverseIndex>>,
}

impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
fn tcx<'c>(&'c self) -> TyCtxt<'tcx> {
self.infcx.tcx
}

fn fold_binder<T: TypeFoldable<'tcx>>(
&mut self,
t: ty::Binder<'tcx, T>,
) -> ty::Binder<'tcx, T> {
self.universes.push(None);
let t = t.super_fold_with(self);
self.universes.pop();
t
}

#[instrument(level = "debug", skip(self))]
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
if !ty.has_projections() {
Expand Down Expand Up @@ -204,6 +216,80 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
}
}
}
ty::Projection(data) if !data.trait_ref(self.infcx.tcx).has_escaping_bound_vars() => {
// See note in `rustc_trait_selection::traits::project`

// One other point mentioning: In `traits::project`, if a
// projection can't be normalized, we return an inference variable
// and register an obligation to later resolve that. Here, the query
// will just return ambiguity. In both cases, the effect is the same: we only want
// to return `ty` because there are bound vars that we aren't yet handling in a more
// complete way.

let tcx = self.infcx.tcx;
let infcx = self.infcx;
let replaced = crate::traits::project::BoundVarReplacer::replace_bound_vars(
infcx,
&mut self.universes,
data,
);
let (data, mapped_regions, mapped_types, mapped_consts) = match replaced {
Some(r) => r,
None => {
bug!("{:?} {:?}", data, self.universes);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh...hmm. interesting. Didn't mean to leave this. But since all the tests pass, maybe the only place that we pass escaping bound vars here is under polymorphization (and supposedly there's no test for the intersection of polymorphization and GATs). But the underlying problem still exists (normalize_erasing_regions). So not sure if it's better to just ICE if we get passed bound vars (gives us test cases) or to fail and return an error (which might still allow us to succeed compilation overall).

Thoughts @nikomatsakis?

//self.error = true;
//return ty.super_fold_with(self);
}
};
let data = data.super_fold_with(self);

let mut orig_values = OriginalQueryValues::default();
// HACK(matthewjasper) `'static` is special-cased in selection,
// so we cannot canonicalize it.
let c_data = self
.infcx
.canonicalize_hr_query_hack(self.param_env.and(data), &mut orig_values);
debug!("QueryNormalizer: c_data = {:#?}", c_data);
debug!("QueryNormalizer: orig_values = {:#?}", orig_values);
let normalized_ty = match tcx.normalize_projection_ty(c_data) {
Ok(result) => {
// We don't expect ambiguity.
if result.is_ambiguous() {
self.error = true;
return ty;
}
match self.infcx.instantiate_query_response_and_region_obligations(
self.cause,
self.param_env,
&orig_values,
result,
) {
Ok(InferOk { value: result, obligations }) => {
debug!("QueryNormalizer: result = {:#?}", result);
debug!("QueryNormalizer: obligations = {:#?}", obligations);
self.obligations.extend(obligations);
result.normalized_ty
}
Err(_) => {
self.error = true;
ty
}
}
}
Err(NoSolution) => {
self.error = true;
ty
}
};
crate::traits::project::PlaceholderReplacer::replace_placeholders(
infcx,
mapped_regions,
mapped_types,
mapped_consts,
&self.universes,
normalized_ty,
)
}

_ => ty,
})();
Expand Down
6 changes: 2 additions & 4 deletions compiler/rustc_typeck/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,6 @@ fn check_associated_item(
}
ty::AssocKind::Fn => {
let sig = fcx.tcx.fn_sig(item.def_id);
let sig = fcx.normalize_associated_types_in(span, sig);
let hir_sig = sig_if_method.expect("bad signature for method");
check_fn_or_method(
fcx,
Expand Down Expand Up @@ -611,7 +610,6 @@ fn check_item_fn(
for_id(tcx, item_id, span).with_fcx(|fcx| {
let def_id = tcx.hir().local_def_id(item_id);
let sig = tcx.fn_sig(def_id);
let sig = fcx.normalize_associated_types_in(span, sig);
let mut implied_bounds = vec![];
check_fn_or_method(fcx, ident.span, sig, decl, def_id.to_def_id(), &mut implied_bounds);
implied_bounds
Expand Down Expand Up @@ -902,8 +900,8 @@ fn check_fn_or_method<'fcx, 'tcx>(
def_id: DefId,
implied_bounds: &mut Vec<Ty<'tcx>>,
) {
let sig = fcx.normalize_associated_types_in(span, sig);
let sig = fcx.tcx.liberate_late_bound_regions(def_id, sig);
let sig = fcx.normalize_associated_types_in(span, sig);

for (&input_ty, ty) in iter::zip(sig.inputs(), hir_decl.inputs) {
fcx.register_wf_obligation(input_ty.into(), ty.span, ObligationCauseCode::MiscObligation);
Expand Down Expand Up @@ -1081,8 +1079,8 @@ fn check_method_receiver<'fcx, 'tcx>(
let span = fn_sig.decl.inputs[0].span;

let sig = fcx.tcx.fn_sig(method.def_id);
let sig = fcx.normalize_associated_types_in(span, sig);
let sig = fcx.tcx.liberate_late_bound_regions(method.def_id, sig);
let sig = fcx.normalize_associated_types_in(span, sig);

debug!("check_method_receiver: sig={:?}", sig);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error[E0277]: the trait bound `Self: Get` is not satisfied
--> $DIR/associated-types-for-unimpl-trait.rs:10:5
--> $DIR/associated-types-for-unimpl-trait.rs:10:8
|
LL | fn uhoh<U:Get>(&self, foo: U, bar: <Self as Get>::Value) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `Self`
| ^^^^ the trait `Get` is not implemented for `Self`
|
help: consider further restricting `Self`
|
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error[E0277]: the trait bound `T: Get` is not satisfied
--> $DIR/associated-types-no-suitable-bound.rs:11:5
--> $DIR/associated-types-no-suitable-bound.rs:11:8
|
LL | fn uhoh<T>(foo: <T as Get>::Value) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `T`
| ^^^^ the trait `Get` is not implemented for `T`
|
help: consider restricting type parameter `T`
|
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error[E0277]: the trait bound `Self: Get` is not satisfied
--> $DIR/associated-types-no-suitable-supertrait-2.rs:17:5
--> $DIR/associated-types-no-suitable-supertrait-2.rs:17:8
|
LL | fn uhoh<U:Get>(&self, foo: U, bar: <Self as Get>::Value) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `Self`
| ^^^^ the trait `Get` is not implemented for `Self`
|
help: consider further restricting `Self`
|
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
error[E0277]: the trait bound `Self: Get` is not satisfied
--> $DIR/associated-types-no-suitable-supertrait.rs:17:5
--> $DIR/associated-types-no-suitable-supertrait.rs:17:8
|
LL | fn uhoh<U:Get>(&self, foo: U, bar: <Self as Get>::Value) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `Self`
| ^^^^ the trait `Get` is not implemented for `Self`
|
help: consider further restricting `Self`
|
LL | fn uhoh<U:Get>(&self, foo: U, bar: <Self as Get>::Value) where Self: Get {}
| ^^^^^^^^^^^^^^^

error[E0277]: the trait bound `(T, U): Get` is not satisfied
--> $DIR/associated-types-no-suitable-supertrait.rs:22:5
--> $DIR/associated-types-no-suitable-supertrait.rs:22:8
|
LL | fn uhoh<U:Get>(&self, foo: U, bar: <(T, U) as Get>::Value) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `(T, U)`
| ^^^^ the trait `Get` is not implemented for `(T, U)`

error: aborting due to 2 previous errors

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error[E0277]: the trait bound `Self: Get` is not satisfied
--> $DIR/associated-types-projection-to-unrelated-trait-in-method-without-default.rs:10:5
--> $DIR/associated-types-projection-to-unrelated-trait-in-method-without-default.rs:10:8
|
LL | fn okay<U:Get>(&self, foo: U, bar: <Self as Get>::Value);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Get` is not implemented for `Self`
| ^^^^ the trait `Get` is not implemented for `Self`
|
help: consider further restricting `Self`
|
Expand Down
36 changes: 36 additions & 0 deletions src/test/ui/associated-types/normalization-debruijn-1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// build-pass
// edition:2018

// Regression test to ensure we handle debruijn indices correctly in projection
// normalization under binders. Found in crater run for #85499

use std::future::Future;
use std::pin::Pin;
pub enum Outcome<S, E> {
Success((S, E)),
}
pub struct Request<'r> {
_marker: std::marker::PhantomData<&'r ()>,
}
pub trait FromRequest<'r>: Sized {
type Error;
fn from_request<'life0>(
request: &'r Request<'life0>,
) -> Pin<Box<dyn Future<Output = Outcome<Self, Self::Error>>>>;
}
impl<'r, T: FromRequest<'r>> FromRequest<'r> for Option<T> {
type Error = ();
fn from_request<'life0>(
request: &'r Request<'life0>,
) -> Pin<Box<dyn Future<Output = Outcome<Self, Self::Error>>>> {
Box::pin(async move {
let request = request;
match T::from_request(request).await {
_ => todo!(),
}
});
todo!()
}
}

fn main() {}
31 changes: 31 additions & 0 deletions src/test/ui/associated-types/normalization-debruijn-2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// build-pass
// edition:2018

// Regression test to ensure we handle debruijn indices correctly in projection
// normalization under binders. Found in crater run for #85499

use std::future::Future;
use std::pin::Pin;
pub enum Outcome<S, E> {
Success(S),
Failure(E),
}
pub struct Request<'r> {
_marker: std::marker::PhantomData<&'r ()>,
}
pub trait FromRequest<'r>: Sized {
type Error;
fn from_request<'life0>(
request: &'r Request<'life0>,
) -> Pin<Box<dyn Future<Output = Outcome<Self, Self::Error>>>>;
}
pub struct S<T> {
_marker: std::marker::PhantomData<T>,
}
impl<'r, T: FromRequest<'r>> S<T> {
pub async fn from_request(request: &'r Request<'_>) {
let _ = T::from_request(request).await;
}
}

fn main() {}
41 changes: 41 additions & 0 deletions src/test/ui/associated-types/normalization-debruijn-3.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// build-pass
// edition:2018

// Regression test to ensure we handle debruijn indices correctly in projection
// normalization under binders. Found in crater run for #85499

use std::future::{Future, Ready};
async fn read() {
let _ = connect(&()).await;
}
async fn connect<A: ToSocketAddr>(addr: A) {
let _ = addr.to_socket_addr().await;
}
pub trait ToSocketAddr {
type Future: Future<Output = ()>;
fn to_socket_addr(&self) -> Self::Future;
}
impl ToSocketAddr for &() {
type Future = Ready<()>;
fn to_socket_addr(&self) -> Self::Future {
unimplemented!()
}
}
struct Server;
impl Server {
fn and_then<F>(self, _fun: F) -> AndThen<F> {
unimplemented!()
}
}
struct AndThen<F> {
_marker: std::marker::PhantomData<F>,
}
pub async fn run<F>(_: F) {
}
fn main() {
let _ = async {
let server = Server;
let verification_route = server.and_then(read);
run(verification_route).await;
};
}
36 changes: 36 additions & 0 deletions src/test/ui/associated-types/normalization-generality.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// build-pass

// Ensures that we don't regress on "implementation is not general enough" when
// normalizating under binders.

#![feature(no_core)]

pub trait Yokeable<'a> {
type Output: 'a;
}

pub struct Yoke<Y: for<'a> Yokeable<'a>> {
_yokeable: Y,
}

impl<Y: for<'a> Yokeable<'a>> Yoke<Y> {
pub fn project<'this, P>(
&'this self,
_f: for<'a> fn(<Y as Yokeable<'a>>::Output, &'a ()) -> <P as Yokeable<'a>>::Output,
) -> Yoke<P>
where
P: for<'a> Yokeable<'a>,
{
unimplemented!()
}
}

pub fn slice(y: Yoke<&'static ()>) -> Yoke<&'static ()> {
y.project(move |yk, _| yk)
}

impl<'a, T> Yokeable<'a> for &'static T {
type Output = &'a T;
}

fn main() {}
Loading