Skip to content

Commit

Permalink
Flounder in function trait when inference var or type alias is encoun…
Browse files Browse the repository at this point in the history
…tered
  • Loading branch information
Aaron1011 committed Jun 8, 2020
1 parent f3763f3 commit 1f6ea3a
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 15 deletions.
4 changes: 2 additions & 2 deletions chalk-solve/src/clauses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ fn program_clauses_that_could_match<I: Interner>(
}

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 {
Expand Down Expand Up @@ -370,7 +370,7 @@ fn program_clauses_that_could_match<I: Interner>(
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(
Expand Down
9 changes: 7 additions & 2 deletions chalk-solve/src/clauses/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<V>(&mut self, binders: &Binders<V>, op: impl FnOnce(&mut Self, V::Result))
pub fn push_binders<R, V>(
&mut self,
binders: &Binders<V>,
op: impl FnOnce(&mut Self, V::Result) -> R,
) -> R
where
V: Fold<I> + HasInterner<Interner = I>,
V::Result: std::fmt::Debug,
Expand All @@ -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
Expand Down
16 changes: 9 additions & 7 deletions chalk-solve/src/clauses/builtin_traits.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -14,7 +14,7 @@ pub fn add_builtin_program_clauses<I: Interner>(
builder: &mut ClauseBuilder<'_, I>,
well_known: WellKnownTrait,
trait_ref: &TraitRef<I>,
) {
) -> 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);
Expand All @@ -26,20 +26,21 @@ pub fn add_builtin_program_clauses<I: Interner>(
if force_impl {
builder.push_fact(trait_ref.clone());
}
return;
return Ok(());
}

match well_known {
WellKnownTrait::Sized => sized::add_sized_program_clauses(db, builder, &trait_ref, ty),
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
Expand All @@ -49,14 +50,15 @@ pub fn add_builtin_assoc_program_clauses<I: Interner>(
builder: &mut ClauseBuilder<'_, I>,
well_known: WellKnownTrait,
self_ty: Ty<I>,
) {
) -> 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
Expand Down
11 changes: 7 additions & 4 deletions chalk-solve/src/clauses/builtin_traits/fn_family.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -21,7 +21,7 @@ pub fn add_fn_trait_program_clauses<I: Interner>(
builder: &mut ClauseBuilder<'_, I>,
trait_id: TraitId<I>,
self_ty: Ty<I>,
) {
) -> Result<(), Floundered> {
let interner = db.interner();
match self_ty.data(interner) {
TyData::Function(fn_val) => {
Expand Down Expand Up @@ -81,7 +81,10 @@ pub fn add_fn_trait_program_clauses<I: Interner>(
builder.push_fact(normalize);
}
});
Ok(())
}
_ => {}
// Function traits are non-enumerable
TyData::InferenceVar(..) | TyData::Alias(..) => Err(Floundered),
_ => Ok(()),
}
}
22 changes: 22 additions & 0 deletions tests/test/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ fn function_implement_fn_traits() {

#[lang(fn)]
trait Fn<Args> where Self: FnMut<Args> { }

struct Ty { }

trait Clone { }
opaque type MyOpaque: Clone = Ty;

}

// Simple test: make sure a fully monomorphic type implements FnOnce
Expand Down Expand Up @@ -156,6 +162,22 @@ fn function_implement_fn_traits() {
} yields {
"No possible solution"
}

// Tests that we flounder for inference variables
goal {
exists<T> {
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"
}
}
}

Expand Down

0 comments on commit 1f6ea3a

Please sign in to comment.