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

Add support for inherent projections in new solver #113336

Merged
merged 2 commits into from
Jul 17, 2023
Merged
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
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1313,7 +1313,7 @@ impl<'tcx> AliasTy<'tcx> {
/// I_i impl subst
/// P_j GAT subst
/// ```
pub fn rebase_args_onto_impl(
pub fn rebase_inherent_args_onto_impl(
self,
impl_args: ty::GenericArgsRef<'tcx>,
tcx: TyCtxt<'tcx>,
Expand Down
44 changes: 44 additions & 0 deletions compiler/rustc_trait_selection/src/solve/inherent_projection.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
use rustc_middle::ty;

use super::EvalCtxt;

impl<'tcx> EvalCtxt<'_, 'tcx> {
pub(super) fn normalize_inherent_associated_type(
&mut self,
goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>,
) -> QueryResult<'tcx> {
let tcx = self.tcx();
let inherent = goal.predicate.projection_ty;
let expected = goal.predicate.term.ty().expect("inherent consts are treated separately");

let impl_def_id = tcx.parent(inherent.def_id);
let impl_substs = self.fresh_args_for_item(impl_def_id);

// Equate impl header and add impl where clauses
self.eq(
goal.param_env,
inherent.self_ty(),
tcx.type_of(impl_def_id).instantiate(tcx, impl_substs),
)?;

// Equate IAT with the RHS of the project goal
let inherent_substs = inherent.rebase_inherent_args_onto_impl(impl_substs, tcx);
self.eq(
goal.param_env,
expected,
tcx.type_of(inherent.def_id).instantiate(tcx, inherent_substs),
)
.expect("expected goal term to be fully unconstrained");

// Check both where clauses on the impl and IAT
self.add_goals(
tcx.predicates_of(inherent.def_id)
.instantiate(tcx, inherent_substs)
.into_iter()
.map(|(pred, _)| goal.with(tcx, pred)),
);

self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
}
1 change: 1 addition & 0 deletions compiler/rustc_trait_selection/src/solve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ mod assembly;
mod canonicalize;
mod eval_ctxt;
mod fulfill;
mod inherent_projection;
pub mod inspect;
mod normalize;
mod opaques;
Expand Down
18 changes: 17 additions & 1 deletion compiler/rustc_trait_selection/src/solve/project_goals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
self.merge_candidates(candidates)
}
ty::AssocItemContainer::ImplContainer => {
bug!("IATs not supported here yet")
self.normalize_inherent_associated_type(goal)
}
}
} else {
Expand Down Expand Up @@ -112,6 +112,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
) -> QueryResult<'tcx> {
if let Some(projection_pred) = assumption.as_projection_clause() {
if projection_pred.projection_def_id() == goal.predicate.def_id() {
let tcx = ecx.tcx();
ecx.probe_candidate("assumption").enter(|ecx| {
let assumption_projection_pred =
ecx.instantiate_binder_with_infer(projection_pred);
Expand All @@ -122,6 +123,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
)?;
ecx.eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term)
.expect("expected goal term to be fully unconstrained");

// Add GAT where clauses from the trait's definition
Copy link
Member Author

Choose a reason for hiding this comment

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

I found it easier to check the GAT WC here in probe_and_match_goal_against_assumption rather than just doing it for object candidates. This also makes sure we check them correctly for alias bounds, and for most other built-in bounds which end up flowing through this method.

Let me know what you think about this.

Copy link
Contributor

Choose a reason for hiding this comment

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

hm... I don't feel too happy about this but don't really know. It feels potentially unnecessary and might cause issues. But I guess we cna always move it out and check it in less places.

Copy link
Member Author

Choose a reason for hiding this comment

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

I mean, especially moving towards coinduction, I'm not certain if I understand how this isn't going to be necessary. Builtin candidates should be checking their GAT WCs for them to be sound, right?

ecx.add_goals(
tcx.predicates_of(goal.predicate.def_id())
.instantiate_own(tcx, goal.predicate.projection_ty.args)
.map(|(pred, _)| goal.with(tcx, pred)),
);

then(ecx)
})
} else {
Expand Down Expand Up @@ -160,6 +169,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
.map(|pred| goal.with(tcx, pred));
ecx.add_goals(where_clause_bounds);

// Add GAT where clauses from the trait's definition
ecx.add_goals(
tcx.predicates_of(goal.predicate.def_id())
.instantiate_own(tcx, goal.predicate.projection_ty.args)
.map(|(pred, _)| goal.with(tcx, pred)),
);

// In case the associated item is hidden due to specialization, we have to
// return ambiguity this would otherwise be incomplete, resulting in
// unsoundness during coherence (#105782).
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_trait_selection/src/solve/weak_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {

let actual = tcx.type_of(weak_ty.def_id).instantiate(tcx, weak_ty.args);
self.eq(goal.param_env, expected, actual)?;

// Check where clauses
self.add_goals(
tcx.predicates_of(weak_ty.def_id)
.instantiate(tcx, weak_ty.args)
.predicates
.into_iter()
.map(|pred| goal.with(tcx, pred)),
);

self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
}
16 changes: 12 additions & 4 deletions compiler/rustc_trait_selection/src/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1402,9 +1402,17 @@ pub fn compute_inherent_assoc_ty_args<'a, 'b, 'tcx>(
let impl_def_id = tcx.parent(alias_ty.def_id);
let impl_args = selcx.infcx.fresh_args_for_item(cause.span, impl_def_id);

let impl_ty = tcx.type_of(impl_def_id).instantiate(tcx, impl_args);
let impl_ty =
normalize_with_depth_to(selcx, param_env, cause.clone(), depth + 1, impl_ty, obligations);
let mut impl_ty = tcx.type_of(impl_def_id).instantiate(tcx, impl_args);
if !selcx.infcx.next_trait_solver() {
impl_ty = normalize_with_depth_to(
selcx,
param_env,
cause.clone(),
depth + 1,
impl_ty,
obligations,
);
}

// Infer the generic parameters of the impl by unifying the
// impl type with the self type of the projection.
Expand All @@ -1421,7 +1429,7 @@ pub fn compute_inherent_assoc_ty_args<'a, 'b, 'tcx>(
}
}

alias_ty.rebase_args_onto_impl(impl_args, tcx)
alias_ty.rebase_inherent_args_onto_impl(impl_args, tcx)
}

enum Projected<'tcx> {
Expand Down
2 changes: 2 additions & 0 deletions tests/ui/associated-inherent-types/inference.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Testing inference capabilities.
// check-pass
// revisions: current next
//[next] compile-flags: -Ztrait-solver=next

#![feature(inherent_associated_types)]
#![allow(incomplete_features)]
Expand Down
9 changes: 7 additions & 2 deletions tests/ui/traits/new-solver/alias-bound-unsound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,17 @@ trait Foo {

impl Foo for () {
type Item = String where String: Copy;
//~^ ERROR overflow evaluating the requirement `<() as Foo>::Item: Copy`
}

fn main() {
let x = String::from("hello, world");
drop(<() as Foo>::copy_me(&x));
//~^ ERROR the type `&<() as Foo>::Item` is not well-formed
//~| ERROR `<() as Foo>::Item` is not well-formed
//~^ ERROR overflow evaluating the requirement `<() as Foo>::Item: Sized`
//~| ERROR overflow evaluating the requirement `<() as Foo>::Item == _`
//~| ERROR overflow evaluating the requirement `<() as Foo>::Item well-formed`
//~| ERROR overflow evaluating the requirement `String <: <() as Foo>::Item`
//~| ERROR overflow evaluating the requirement `&<() as Foo>::Item well-formed`
//~| ERROR type annotations needed
println!("{x}");
}
65 changes: 60 additions & 5 deletions tests/ui/traits/new-solver/alias-bound-unsound.stderr
Original file line number Diff line number Diff line change
@@ -1,14 +1,69 @@
error: the type `&<() as Foo>::Item` is not well-formed
--> $DIR/alias-bound-unsound.rs:23:31
error[E0275]: overflow evaluating the requirement `<() as Foo>::Item: Copy`
--> $DIR/alias-bound-unsound.rs:18:17
|
LL | type Item = String where String: Copy;
| ^^^^^^
|
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`alias_bound_unsound`)
note: required by a bound in `Foo::Item`
--> $DIR/alias-bound-unsound.rs:8:16
|
LL | type Item: Copy
| ^^^^ required by this bound in `Foo::Item`

error[E0282]: type annotations needed
--> $DIR/alias-bound-unsound.rs:24:5
|
LL | drop(<() as Foo>::copy_me(&x));
| ^^^^ cannot infer type of the type parameter `T` declared on the function `drop`
|
help: consider specifying the generic argument
|
LL | drop::<T>(<() as Foo>::copy_me(&x));
| +++++

error[E0275]: overflow evaluating the requirement `&<() as Foo>::Item well-formed`
--> $DIR/alias-bound-unsound.rs:24:31
|
LL | drop(<() as Foo>::copy_me(&x));
| ^^
|
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`alias_bound_unsound`)

error: the type `<() as Foo>::Item` is not well-formed
--> $DIR/alias-bound-unsound.rs:23:10
error[E0275]: overflow evaluating the requirement `String <: <() as Foo>::Item`
--> $DIR/alias-bound-unsound.rs:24:31
|
LL | drop(<() as Foo>::copy_me(&x));
| ^^
|
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`alias_bound_unsound`)

error[E0275]: overflow evaluating the requirement `<() as Foo>::Item well-formed`
--> $DIR/alias-bound-unsound.rs:24:10
|
LL | drop(<() as Foo>::copy_me(&x));
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`alias_bound_unsound`)

error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _`
--> $DIR/alias-bound-unsound.rs:24:10
|
LL | drop(<() as Foo>::copy_me(&x));
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`alias_bound_unsound`)

error[E0275]: overflow evaluating the requirement `<() as Foo>::Item: Sized`
--> $DIR/alias-bound-unsound.rs:24:10
|
LL | drop(<() as Foo>::copy_me(&x));
| ^^^^^^^^^^^^^^^^^^^^
|
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`alias_bound_unsound`)
= note: the return type of a function must have a statically known size

error: aborting due to 2 previous errors
error: aborting due to 7 previous errors

Some errors have detailed explanations: E0275, E0282.
For more information about an error, try `rustc --explain E0275`.