about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2022-02-18 23:23:09 +0100
committerGitHub <noreply@github.com>2022-02-18 23:23:09 +0100
commit1e2f63de0a5e9a32d97d355f6351665e77455be2 (patch)
treee9da306098401bf54ecc4feff35942c30bd5eab1
parentcb35370557ecea46d7b36237b9c60bfa105c8de5 (diff)
parent879e4f8131b71050b00407befd6f1669389fe9ed (diff)
downloadrust-1e2f63de0a5e9a32d97d355f6351665e77455be2.tar.gz
rust-1e2f63de0a5e9a32d97d355f6351665e77455be2.zip
Rollup merge of #93892 - compiler-errors:issue-92917, r=jackh726,nikomatsakis
Only mark projection as ambiguous if GAT substs are constrained

A slightly more targeted version of #92917, where we only give up with ambiguity if we infer something about the GATs substs when probing for a projection candidate.

fixes #93874
also note (but like the previous PR, does not fix) #91762

r? `@jackh726`
cc `@nikomatsakis` who reviewed #92917
-rw-r--r--compiler/rustc_infer/src/infer/type_variable.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs58
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs38
-rw-r--r--src/test/ui/generic-associated-types/issue-74824.rs1
-rw-r--r--src/test/ui/generic-associated-types/issue-74824.stderr11
-rw-r--r--src/test/ui/generic-associated-types/issue-93874.rs35
6 files changed, 101 insertions, 44 deletions
diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs
index 0864edf4451..74c44089045 100644
--- a/compiler/rustc_infer/src/infer/type_variable.rs
+++ b/compiler/rustc_infer/src/infer/type_variable.rs
@@ -263,7 +263,7 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> {
         let index = self.values().push(TypeVariableData { origin });
         assert_eq!(eq_key.vid.as_u32(), index as u32);
 
-        debug!("new_var(index={:?}, universe={:?}, origin={:?}", eq_key.vid, universe, origin,);
+        debug!("new_var(index={:?}, universe={:?}, origin={:?})", eq_key.vid, universe, origin);
 
         eq_key.vid
     }
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index f5d531e631e..dba24fb2f31 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -19,6 +19,7 @@ use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey};
 use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use crate::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime};
 use crate::traits::error_reporting::InferCtxtExt as _;
+use crate::traits::select::ProjectionMatchesProjection;
 use rustc_data_structures::sso::SsoHashSet;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::ErrorReported;
@@ -1075,16 +1076,6 @@ fn project<'cx, 'tcx>(
         return Ok(Projected::Progress(Progress::error(selcx.tcx())));
     }
 
-    // If the obligation contains any inference types or consts in associated
-    // type substs, then we don't assemble any candidates.
-    // This isn't really correct, but otherwise we can end up in a case where
-    // we constrain inference variables by selecting a single predicate, when
-    // we need to stay general. See issue #91762.
-    let (_, predicate_own_substs) = obligation.predicate.trait_ref_and_own_substs(selcx.tcx());
-    if predicate_own_substs.iter().any(|g| g.has_infer_types_or_consts()) {
-        return Err(ProjectionError::TooManyCandidates);
-    }
-
     let mut candidates = ProjectionCandidateSet::None;
 
     // Make sure that the following procedures are kept in order. ParamEnv
@@ -1182,7 +1173,7 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>(
         ProjectionCandidate::TraitDef,
         bounds.iter(),
         true,
-    )
+    );
 }
 
 /// In the case of a trait object like
@@ -1247,28 +1238,35 @@ fn assemble_candidates_from_predicates<'cx, 'tcx>(
         let bound_predicate = predicate.kind();
         if let ty::PredicateKind::Projection(data) = predicate.kind().skip_binder() {
             let data = bound_predicate.rebind(data);
-            let same_def_id = data.projection_def_id() == obligation.predicate.item_def_id;
-
-            let is_match = same_def_id
-                && infcx.probe(|_| {
-                    selcx.match_projection_projections(
-                        obligation,
-                        data,
-                        potentially_unnormalized_candidates,
-                    )
-                });
+            if data.projection_def_id() != obligation.predicate.item_def_id {
+                continue;
+            }
 
-            if is_match {
-                candidate_set.push_candidate(ctor(data));
+            let is_match = infcx.probe(|_| {
+                selcx.match_projection_projections(
+                    obligation,
+                    data,
+                    potentially_unnormalized_candidates,
+                )
+            });
 
-                if potentially_unnormalized_candidates
-                    && !obligation.predicate.has_infer_types_or_consts()
-                {
-                    // HACK: Pick the first trait def candidate for a fully
-                    // inferred predicate. This is to allow duplicates that
-                    // differ only in normalization.
-                    return;
+            match is_match {
+                ProjectionMatchesProjection::Yes => {
+                    candidate_set.push_candidate(ctor(data));
+
+                    if potentially_unnormalized_candidates
+                        && !obligation.predicate.has_infer_types_or_consts()
+                    {
+                        // HACK: Pick the first trait def candidate for a fully
+                        // inferred predicate. This is to allow duplicates that
+                        // differ only in normalization.
+                        return;
+                    }
+                }
+                ProjectionMatchesProjection::Ambiguous => {
+                    candidate_set.mark_ambiguous();
                 }
+                ProjectionMatchesProjection::No => {}
             }
         }
     }
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 64af875dd22..3b69700530b 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -1494,12 +1494,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         })
     }
 
+    /// Return `Yes` if the obligation's predicate type applies to the env_predicate, and
+    /// `No` if it does not. Return `Ambiguous` in the case that the projection type is a GAT,
+    /// and applying this env_predicate constrains any of the obligation's GAT substitutions.
+    ///
+    /// This behavior is a somewhat of a hack to prevent overconstraining inference variables
+    /// in cases like #91762.
     pub(super) fn match_projection_projections(
         &mut self,
         obligation: &ProjectionTyObligation<'tcx>,
         env_predicate: PolyProjectionPredicate<'tcx>,
         potentially_unnormalized_candidates: bool,
-    ) -> bool {
+    ) -> ProjectionMatchesProjection {
         let mut nested_obligations = Vec::new();
         let (infer_predicate, _) = self.infcx.replace_bound_vars_with_fresh_vars(
             obligation.cause.span,
@@ -1521,7 +1527,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             infer_predicate.projection_ty
         };
 
-        self.infcx
+        let is_match = self
+            .infcx
             .at(&obligation.cause, obligation.param_env)
             .sup(obligation.predicate, infer_projection)
             .map_or(false, |InferOk { obligations, value: () }| {
@@ -1530,7 +1537,26 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     nested_obligations.into_iter().chain(obligations),
                 )
                 .map_or(false, |res| res.may_apply())
-            })
+            });
+
+        if is_match {
+            let generics = self.tcx().generics_of(obligation.predicate.item_def_id);
+            // FIXME(generic-associated-types): Addresses aggressive inference in #92917.
+            // If this type is a GAT, and of the GAT substs resolve to something new,
+            // that means that we must have newly inferred something about the GAT.
+            // We should give up in that case.
+            if !generics.params.is_empty()
+                && obligation.predicate.substs[generics.parent_count..]
+                    .iter()
+                    .any(|&p| p.has_infer_types_or_consts() && self.infcx.shallow_resolve(p) != p)
+            {
+                ProjectionMatchesProjection::Ambiguous
+            } else {
+                ProjectionMatchesProjection::Yes
+            }
+        } else {
+            ProjectionMatchesProjection::No
+        }
     }
 
     ///////////////////////////////////////////////////////////////////////////
@@ -2709,3 +2735,9 @@ impl<'o, 'tcx> fmt::Debug for TraitObligationStack<'o, 'tcx> {
         write!(f, "TraitObligationStack({:?})", self.obligation)
     }
 }
+
+pub enum ProjectionMatchesProjection {
+    Yes,
+    Ambiguous,
+    No,
+}
diff --git a/src/test/ui/generic-associated-types/issue-74824.rs b/src/test/ui/generic-associated-types/issue-74824.rs
index 01f99fa4487..1bbf7aac5cd 100644
--- a/src/test/ui/generic-associated-types/issue-74824.rs
+++ b/src/test/ui/generic-associated-types/issue-74824.rs
@@ -17,7 +17,6 @@ impl<T> UnsafeCopy for T {}
 fn main() {
     let b = Box::new(42usize);
     let copy = <()>::copy(&b);
-    //~^ type annotations needed
 
     let raw_b = Box::deref(&b) as *const _;
     let raw_copy = Box::deref(&copy) as *const _;
diff --git a/src/test/ui/generic-associated-types/issue-74824.stderr b/src/test/ui/generic-associated-types/issue-74824.stderr
index e7ebf5964ba..8517eb9fa21 100644
--- a/src/test/ui/generic-associated-types/issue-74824.stderr
+++ b/src/test/ui/generic-associated-types/issue-74824.stderr
@@ -27,13 +27,6 @@ help: consider restricting type parameter `T`
 LL |     type Copy<T: std::clone::Clone>: Copy = Box<T>;
    |                +++++++++++++++++++
 
-error[E0282]: type annotations needed
-  --> $DIR/issue-74824.rs:19:16
-   |
-LL |     let copy = <()>::copy(&b);
-   |                ^^^^^^^^^^ cannot infer type for type parameter `T` declared on the associated function `copy`
-
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
 
-Some errors have detailed explanations: E0277, E0282.
-For more information about an error, try `rustc --explain E0277`.
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/generic-associated-types/issue-93874.rs b/src/test/ui/generic-associated-types/issue-93874.rs
new file mode 100644
index 00000000000..f403d75167d
--- /dev/null
+++ b/src/test/ui/generic-associated-types/issue-93874.rs
@@ -0,0 +1,35 @@
+// check-pass
+
+#![feature(generic_associated_types)]
+
+pub trait Build {
+    type Output<O>;
+    fn build<O>(self, input: O) -> Self::Output<O>;
+}
+
+pub struct IdentityBuild;
+impl Build for IdentityBuild {
+    type Output<O> = O;
+    fn build<O>(self, input: O) -> Self::Output<O> {
+        input
+    }
+}
+
+fn a() {
+    let _x: u8 = IdentityBuild.build(10);
+}
+
+fn b() {
+    let _x: Vec<u8> = IdentityBuild.build(Vec::new());
+}
+
+fn c() {
+    let mut f = IdentityBuild.build(|| ());
+    (f)();
+}
+
+pub fn main() {
+    a();
+    b();
+    c();
+}