diff --git a/chalk-solve/src/clauses.rs b/chalk-solve/src/clauses.rs index d0394b44107..62e7707eedf 100644 --- a/chalk-solve/src/clauses.rs +++ b/chalk-solve/src/clauses.rs @@ -295,7 +295,7 @@ fn program_clauses_that_could_match( } if let Some(well_known) = trait_datum.well_known { - builtin_traits::add_builtin_program_clauses(db, builder, well_known, trait_ref); + builtin_traits::add_builtin_program_clauses(db, builder, well_known, trait_ref)?; } } DomainGoal::Holds(WhereClause::AliasEq(alias_eq)) => match &alias_eq.alias { @@ -370,7 +370,7 @@ fn program_clauses_that_could_match( if let Some(well_known) = trait_datum.well_known { builtin_traits::add_builtin_assoc_program_clauses( db, builder, well_known, self_ty, - ); + )?; } push_program_clauses_for_associated_type_values_in_impls_of( diff --git a/chalk-solve/src/clauses/builder.rs b/chalk-solve/src/clauses/builder.rs index 62880c107de..d9ab809da78 100644 --- a/chalk-solve/src/clauses/builder.rs +++ b/chalk-solve/src/clauses/builder.rs @@ -114,7 +114,11 @@ impl<'me, I: Interner> ClauseBuilder<'me, I> { /// The new binders are always pushed onto the end of the internal /// list of binders; this means that any extant values where were /// created referencing the *old* list of binders are still valid. - pub fn push_binders(&mut self, binders: &Binders, op: impl FnOnce(&mut Self, V::Result)) + pub fn push_binders( + &mut self, + binders: &Binders, + op: impl FnOnce(&mut Self, V::Result) -> R, + ) -> R where V: Fold + HasInterner, V::Result: std::fmt::Debug, @@ -134,10 +138,11 @@ impl<'me, I: Interner> ClauseBuilder<'me, I> { let value = binders.substitute(self.interner(), &self.parameters[old_len..]); debug!("push_binders: value={:?}", value); - op(self, value); + let res = op(self, value); self.binders.truncate(old_len); self.parameters.truncate(old_len); + res } /// Push a single binder, for a type, at the end of the binder diff --git a/chalk-solve/src/clauses/builtin_traits.rs b/chalk-solve/src/clauses/builtin_traits.rs index 05111bcaaab..4a4259695cd 100644 --- a/chalk-solve/src/clauses/builtin_traits.rs +++ b/chalk-solve/src/clauses/builtin_traits.rs @@ -1,6 +1,6 @@ use super::{builder::ClauseBuilder, generalize}; use crate::{Interner, RustIrDatabase, TraitRef, WellKnownTrait}; -use chalk_ir::{Substitution, Ty}; +use chalk_ir::{Floundered, Substitution, Ty}; mod clone; mod copy; @@ -14,7 +14,7 @@ pub fn add_builtin_program_clauses( builder: &mut ClauseBuilder<'_, I>, well_known: WellKnownTrait, trait_ref: &TraitRef, -) { +) -> Result<(), Floundered> { // If `trait_ref` contains bound vars, we want to universally quantify them. // `Generalize` collects them for us. let generalized = generalize::Generalize::apply(db.interner(), trait_ref); @@ -26,7 +26,7 @@ pub fn add_builtin_program_clauses( if force_impl { builder.push_fact(trait_ref.clone()); } - return; + return Ok(()); } match well_known { @@ -34,12 +34,13 @@ pub fn add_builtin_program_clauses( WellKnownTrait::Copy => copy::add_copy_program_clauses(db, builder, &trait_ref, ty), WellKnownTrait::Clone => clone::add_clone_program_clauses(db, builder, &trait_ref, ty), WellKnownTrait::FnOnce | WellKnownTrait::FnMut | WellKnownTrait::Fn => { - fn_family::add_fn_trait_program_clauses(db, builder, trait_ref.trait_id, self_ty) + fn_family::add_fn_trait_program_clauses(db, builder, trait_ref.trait_id, self_ty)? } // Drop impls are provided explicitly WellKnownTrait::Drop => (), } - }); + Ok(()) + }) } /// Like `add_builtin_program_clauses`, but for `DomainGoal::Normalize` involving @@ -49,14 +50,15 @@ pub fn add_builtin_assoc_program_clauses( builder: &mut ClauseBuilder<'_, I>, well_known: WellKnownTrait, self_ty: Ty, -) { +) -> Result<(), Floundered> { match well_known { WellKnownTrait::FnOnce => { let trait_id = db.well_known_trait_id(well_known).unwrap(); - fn_family::add_fn_trait_program_clauses(db, builder, trait_id, self_ty); + fn_family::add_fn_trait_program_clauses(db, builder, trait_id, self_ty)?; } _ => {} } + Ok(()) } /// Given a trait ref `T0: Trait` and a list of types `U0..Un`, pushes a clause of the form diff --git a/chalk-solve/src/clauses/builtin_traits/fn_family.rs b/chalk-solve/src/clauses/builtin_traits/fn_family.rs index b4f388006a3..12c86516ba2 100644 --- a/chalk-solve/src/clauses/builtin_traits/fn_family.rs +++ b/chalk-solve/src/clauses/builtin_traits/fn_family.rs @@ -3,8 +3,8 @@ use crate::infer::instantiate::IntoBindersAndValue; use crate::rust_ir::WellKnownTrait; use crate::{Interner, RustIrDatabase, TraitRef}; use chalk_ir::{ - AliasTy, ApplicationTy, Binders, Normalize, ProjectionTy, Substitution, TraitId, Ty, TyData, - TypeName, VariableKinds, + AliasTy, ApplicationTy, Binders, Floundered, Normalize, ProjectionTy, Substitution, TraitId, + Ty, TyData, TypeName, VariableKinds, }; /// Handles clauses for FnOnce/FnMut/Fn. @@ -21,7 +21,7 @@ pub fn add_fn_trait_program_clauses( builder: &mut ClauseBuilder<'_, I>, trait_id: TraitId, self_ty: Ty, -) { +) -> Result<(), Floundered> { let interner = db.interner(); match self_ty.data(interner) { TyData::Function(fn_val) => { @@ -81,7 +81,10 @@ pub fn add_fn_trait_program_clauses( builder.push_fact(normalize); } }); + Ok(()) } - _ => {} + // Function traits are non-enumerable + TyData::InferenceVar(..) | TyData::Alias(..) => Err(Floundered), + _ => Ok(()), } } diff --git a/tests/test/functions.rs b/tests/test/functions.rs index ec8e7eebdd9..058e35d80ac 100644 --- a/tests/test/functions.rs +++ b/tests/test/functions.rs @@ -14,6 +14,12 @@ fn function_implement_fn_traits() { #[lang(fn)] trait Fn where Self: FnMut { } + + struct Ty { } + + trait Clone { } + opaque type MyOpaque: Clone = Ty; + } // Simple test: make sure a fully monomorphic type implements FnOnce @@ -156,6 +162,22 @@ fn function_implement_fn_traits() { } yields { "No possible solution" } + + // Tests that we flounder for inference variables + goal { + exists { + T: FnOnce<()> + } + } yields_first[SolverChoice::slg(3, None)] { + "Floundered" + } + + // Tests that we flounder for alias type (opaque) + goal { + MyOpaque: FnOnce<()> + } yields_first[SolverChoice::slg(3, None)] { + "Floundered" + } } }