Skip to content

Commit 1e2f63d

Browse files
authored
Rollup merge of rust-lang#93892 - compiler-errors:issue-92917, r=jackh726,nikomatsakis
Only mark projection as ambiguous if GAT substs are constrained A slightly more targeted version of rust-lang#92917, where we only give up with ambiguity if we infer something about the GATs substs when probing for a projection candidate. fixes rust-lang#93874 also note (but like the previous PR, does not fix) rust-lang#91762 r? `@jackh726` cc `@nikomatsakis` who reviewed rust-lang#92917
2 parents cb35370 + 879e4f8 commit 1e2f63d

File tree

6 files changed

+101
-44
lines changed

6 files changed

+101
-44
lines changed

compiler/rustc_infer/src/infer/type_variable.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> {
263263
let index = self.values().push(TypeVariableData { origin });
264264
assert_eq!(eq_key.vid.as_u32(), index as u32);
265265

266-
debug!("new_var(index={:?}, universe={:?}, origin={:?}", eq_key.vid, universe, origin,);
266+
debug!("new_var(index={:?}, universe={:?}, origin={:?})", eq_key.vid, universe, origin);
267267

268268
eq_key.vid
269269
}

compiler/rustc_trait_selection/src/traits/project.rs

+28-30
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey};
1919
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
2020
use crate::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime};
2121
use crate::traits::error_reporting::InferCtxtExt as _;
22+
use crate::traits::select::ProjectionMatchesProjection;
2223
use rustc_data_structures::sso::SsoHashSet;
2324
use rustc_data_structures::stack::ensure_sufficient_stack;
2425
use rustc_errors::ErrorReported;
@@ -1075,16 +1076,6 @@ fn project<'cx, 'tcx>(
10751076
return Ok(Projected::Progress(Progress::error(selcx.tcx())));
10761077
}
10771078

1078-
// If the obligation contains any inference types or consts in associated
1079-
// type substs, then we don't assemble any candidates.
1080-
// This isn't really correct, but otherwise we can end up in a case where
1081-
// we constrain inference variables by selecting a single predicate, when
1082-
// we need to stay general. See issue #91762.
1083-
let (_, predicate_own_substs) = obligation.predicate.trait_ref_and_own_substs(selcx.tcx());
1084-
if predicate_own_substs.iter().any(|g| g.has_infer_types_or_consts()) {
1085-
return Err(ProjectionError::TooManyCandidates);
1086-
}
1087-
10881079
let mut candidates = ProjectionCandidateSet::None;
10891080

10901081
// Make sure that the following procedures are kept in order. ParamEnv
@@ -1182,7 +1173,7 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>(
11821173
ProjectionCandidate::TraitDef,
11831174
bounds.iter(),
11841175
true,
1185-
)
1176+
);
11861177
}
11871178

11881179
/// In the case of a trait object like
@@ -1247,28 +1238,35 @@ fn assemble_candidates_from_predicates<'cx, 'tcx>(
12471238
let bound_predicate = predicate.kind();
12481239
if let ty::PredicateKind::Projection(data) = predicate.kind().skip_binder() {
12491240
let data = bound_predicate.rebind(data);
1250-
let same_def_id = data.projection_def_id() == obligation.predicate.item_def_id;
1251-
1252-
let is_match = same_def_id
1253-
&& infcx.probe(|_| {
1254-
selcx.match_projection_projections(
1255-
obligation,
1256-
data,
1257-
potentially_unnormalized_candidates,
1258-
)
1259-
});
1241+
if data.projection_def_id() != obligation.predicate.item_def_id {
1242+
continue;
1243+
}
12601244

1261-
if is_match {
1262-
candidate_set.push_candidate(ctor(data));
1245+
let is_match = infcx.probe(|_| {
1246+
selcx.match_projection_projections(
1247+
obligation,
1248+
data,
1249+
potentially_unnormalized_candidates,
1250+
)
1251+
});
12631252

1264-
if potentially_unnormalized_candidates
1265-
&& !obligation.predicate.has_infer_types_or_consts()
1266-
{
1267-
// HACK: Pick the first trait def candidate for a fully
1268-
// inferred predicate. This is to allow duplicates that
1269-
// differ only in normalization.
1270-
return;
1253+
match is_match {
1254+
ProjectionMatchesProjection::Yes => {
1255+
candidate_set.push_candidate(ctor(data));
1256+
1257+
if potentially_unnormalized_candidates
1258+
&& !obligation.predicate.has_infer_types_or_consts()
1259+
{
1260+
// HACK: Pick the first trait def candidate for a fully
1261+
// inferred predicate. This is to allow duplicates that
1262+
// differ only in normalization.
1263+
return;
1264+
}
1265+
}
1266+
ProjectionMatchesProjection::Ambiguous => {
1267+
candidate_set.mark_ambiguous();
12711268
}
1269+
ProjectionMatchesProjection::No => {}
12721270
}
12731271
}
12741272
}

compiler/rustc_trait_selection/src/traits/select/mod.rs

+35-3
Original file line numberDiff line numberDiff line change
@@ -1494,12 +1494,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
14941494
})
14951495
}
14961496

1497+
/// Return `Yes` if the obligation's predicate type applies to the env_predicate, and
1498+
/// `No` if it does not. Return `Ambiguous` in the case that the projection type is a GAT,
1499+
/// and applying this env_predicate constrains any of the obligation's GAT substitutions.
1500+
///
1501+
/// This behavior is a somewhat of a hack to prevent overconstraining inference variables
1502+
/// in cases like #91762.
14971503
pub(super) fn match_projection_projections(
14981504
&mut self,
14991505
obligation: &ProjectionTyObligation<'tcx>,
15001506
env_predicate: PolyProjectionPredicate<'tcx>,
15011507
potentially_unnormalized_candidates: bool,
1502-
) -> bool {
1508+
) -> ProjectionMatchesProjection {
15031509
let mut nested_obligations = Vec::new();
15041510
let (infer_predicate, _) = self.infcx.replace_bound_vars_with_fresh_vars(
15051511
obligation.cause.span,
@@ -1521,7 +1527,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
15211527
infer_predicate.projection_ty
15221528
};
15231529

1524-
self.infcx
1530+
let is_match = self
1531+
.infcx
15251532
.at(&obligation.cause, obligation.param_env)
15261533
.sup(obligation.predicate, infer_projection)
15271534
.map_or(false, |InferOk { obligations, value: () }| {
@@ -1530,7 +1537,26 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
15301537
nested_obligations.into_iter().chain(obligations),
15311538
)
15321539
.map_or(false, |res| res.may_apply())
1533-
})
1540+
});
1541+
1542+
if is_match {
1543+
let generics = self.tcx().generics_of(obligation.predicate.item_def_id);
1544+
// FIXME(generic-associated-types): Addresses aggressive inference in #92917.
1545+
// If this type is a GAT, and of the GAT substs resolve to something new,
1546+
// that means that we must have newly inferred something about the GAT.
1547+
// We should give up in that case.
1548+
if !generics.params.is_empty()
1549+
&& obligation.predicate.substs[generics.parent_count..]
1550+
.iter()
1551+
.any(|&p| p.has_infer_types_or_consts() && self.infcx.shallow_resolve(p) != p)
1552+
{
1553+
ProjectionMatchesProjection::Ambiguous
1554+
} else {
1555+
ProjectionMatchesProjection::Yes
1556+
}
1557+
} else {
1558+
ProjectionMatchesProjection::No
1559+
}
15341560
}
15351561

15361562
///////////////////////////////////////////////////////////////////////////
@@ -2709,3 +2735,9 @@ impl<'o, 'tcx> fmt::Debug for TraitObligationStack<'o, 'tcx> {
27092735
write!(f, "TraitObligationStack({:?})", self.obligation)
27102736
}
27112737
}
2738+
2739+
pub enum ProjectionMatchesProjection {
2740+
Yes,
2741+
Ambiguous,
2742+
No,
2743+
}

src/test/ui/generic-associated-types/issue-74824.rs

-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ impl<T> UnsafeCopy for T {}
1717
fn main() {
1818
let b = Box::new(42usize);
1919
let copy = <()>::copy(&b);
20-
//~^ type annotations needed
2120

2221
let raw_b = Box::deref(&b) as *const _;
2322
let raw_copy = Box::deref(&copy) as *const _;

src/test/ui/generic-associated-types/issue-74824.stderr

+2-9
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,6 @@ help: consider restricting type parameter `T`
2727
LL | type Copy<T: std::clone::Clone>: Copy = Box<T>;
2828
| +++++++++++++++++++
2929

30-
error[E0282]: type annotations needed
31-
--> $DIR/issue-74824.rs:19:16
32-
|
33-
LL | let copy = <()>::copy(&b);
34-
| ^^^^^^^^^^ cannot infer type for type parameter `T` declared on the associated function `copy`
35-
36-
error: aborting due to 3 previous errors
30+
error: aborting due to 2 previous errors
3731

38-
Some errors have detailed explanations: E0277, E0282.
39-
For more information about an error, try `rustc --explain E0277`.
32+
For more information about this error, try `rustc --explain E0277`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// check-pass
2+
3+
#![feature(generic_associated_types)]
4+
5+
pub trait Build {
6+
type Output<O>;
7+
fn build<O>(self, input: O) -> Self::Output<O>;
8+
}
9+
10+
pub struct IdentityBuild;
11+
impl Build for IdentityBuild {
12+
type Output<O> = O;
13+
fn build<O>(self, input: O) -> Self::Output<O> {
14+
input
15+
}
16+
}
17+
18+
fn a() {
19+
let _x: u8 = IdentityBuild.build(10);
20+
}
21+
22+
fn b() {
23+
let _x: Vec<u8> = IdentityBuild.build(Vec::new());
24+
}
25+
26+
fn c() {
27+
let mut f = IdentityBuild.build(|| ());
28+
(f)();
29+
}
30+
31+
pub fn main() {
32+
a();
33+
b();
34+
c();
35+
}

0 commit comments

Comments
 (0)