From 3027e2227d26021672f76fb5433cc6f33ccaa0fb Mon Sep 17 00:00:00 2001 From: Jack Huey Date: Thu, 11 Jun 2020 21:15:33 -0400 Subject: [PATCH 1/7] Add closures --- chalk-integration/src/db.rs | 27 ++++- chalk-integration/src/lowering.rs | 11 +- chalk-integration/src/program.rs | 26 ++++- chalk-ir/src/debug.rs | 7 ++ chalk-ir/src/fold/boring_impls.rs | 1 + chalk-ir/src/interner.rs | 9 ++ chalk-ir/src/lib.rs | 6 + chalk-ir/src/visit/boring_impls.rs | 9 +- chalk-solve/src/clauses.rs | 3 +- chalk-solve/src/clauses/builtin_traits.rs | 5 +- .../src/clauses/builtin_traits/copy.rs | 18 +++ .../src/clauses/builtin_traits/fn_family.rs | 110 +++++++++++++++++- .../src/clauses/builtin_traits/sized.rs | 1 + chalk-solve/src/lib.rs | 8 ++ chalk-solve/src/rust_ir.rs | 13 +++ tests/integration/panic.rs | 16 +++ tests/test/fn_def.rs | 34 ++++++ 17 files changed, 280 insertions(+), 24 deletions(-) diff --git a/chalk-integration/src/db.rs b/chalk-integration/src/db.rs index b8f5fb7c468..5bf78b1e037 100644 --- a/chalk-integration/src/db.rs +++ b/chalk-integration/src/db.rs @@ -7,12 +7,13 @@ use crate::{ tls, }; use chalk_ir::{ - AdtId, AssocTypeId, Canonical, ConstrainedSubst, Environment, FnDefId, GenericArg, Goal, - ImplId, InEnvironment, OpaqueTyId, ProgramClause, ProgramClauses, TraitId, Ty, UCanonical, + AdtId, AssocTypeId, Canonical, ClosureId, ConstrainedSubst, Environment, FnDefId, GenericArg, + Goal, ImplId, InEnvironment, OpaqueTyId, ProgramClause, ProgramClauses, Substitution, TraitId, + Ty, UCanonical, }; use chalk_solve::rust_ir::{ - AdtDatum, AssociatedTyDatum, AssociatedTyValue, AssociatedTyValueId, FnDefDatum, ImplDatum, - OpaqueTyDatum, TraitDatum, WellKnownTrait, + AdtDatum, AssociatedTyDatum, AssociatedTyValue, AssociatedTyValueId, ClosureDatum, FnDefDatum, + ImplDatum, OpaqueTyDatum, TraitDatum, WellKnownTrait, }; use chalk_solve::{RustIrDatabase, Solution, SolverChoice, SubstitutionResult}; use salsa::Database; @@ -154,4 +155,22 @@ impl RustIrDatabase for ChalkDatabase { fn is_object_safe(&self, trait_id: TraitId) -> bool { self.program_ir().unwrap().is_object_safe(trait_id) } + + fn closure_datum( + &self, + closure_id: ClosureId, + substs: Substitution, + ) -> Arc> { + self.program_ir().unwrap().closure_datum(closure_id, substs) + } + + fn closure_upvars( + &self, + closure_id: ClosureId, + substs: Substitution, + ) -> Substitution { + self.program_ir() + .unwrap() + .closure_upvars(closure_id, substs) + } } diff --git a/chalk-integration/src/lowering.rs b/chalk-integration/src/lowering.rs index 48d43d55ef2..8189ae54018 100644 --- a/chalk-integration/src/lowering.rs +++ b/chalk-integration/src/lowering.rs @@ -132,8 +132,8 @@ impl<'k> Env<'k> { actual: 0, }); } else { - return Ok(chalk_ir::TyData::Function(chalk_ir::Fn { - num_binders: k.binders.len(interner), + return Ok(chalk_ir::TyData::Apply(chalk_ir::ApplicationTy { + name: chalk_ir::TypeName::FnDef(id.clone()), substitution: chalk_ir::Substitution::empty(interner), }) .intern(interner) @@ -1032,11 +1032,12 @@ impl LowerFnDefn for FnDefn { env: &Env, ) -> LowerResult> { let binders = env.in_binders(self.all_parameters(), |env| { - let args: LowerResult<_> = self.argument_types.iter().map(|t| t.lower(env)).collect(); let where_clauses = self.lower_where_clauses(env)?; - let return_type = self.return_type.lower(env)?; - let inputs_and_output = env.in_binders(vec![], |_| { + let inputs_and_output = env.in_binders(vec![], |env| { + let args: LowerResult<_> = + self.argument_types.iter().map(|t| t.lower(env)).collect(); + let return_type = self.return_type.lower(env)?; Ok(rust_ir::FnDefInputsAndOutputDatum { argument_types: args?, return_type, diff --git a/chalk-integration/src/program.rs b/chalk-integration/src/program.rs index db908dc72d1..7988a2dfd3f 100644 --- a/chalk-integration/src/program.rs +++ b/chalk-integration/src/program.rs @@ -3,13 +3,13 @@ use crate::{tls, Identifier, TypeKind}; use chalk_ir::could_match::CouldMatch; use chalk_ir::debug::Angle; use chalk_ir::{ - debug::SeparatorTraitRef, AdtId, AliasTy, ApplicationTy, AssocTypeId, FnDefId, GenericArg, - Goal, Goals, ImplId, Lifetime, OpaqueTy, OpaqueTyId, ProgramClause, ProgramClauseImplication, - ProgramClauses, ProjectionTy, Substitution, TraitId, Ty, + debug::SeparatorTraitRef, AdtId, AliasTy, ApplicationTy, AssocTypeId, ClosureId, FnDefId, + GenericArg, Goal, Goals, ImplId, Lifetime, OpaqueTy, OpaqueTyId, ProgramClause, + ProgramClauseImplication, ProgramClauses, ProjectionTy, Substitution, TraitId, Ty, }; use chalk_solve::rust_ir::{ - AdtDatum, AssociatedTyDatum, AssociatedTyValue, AssociatedTyValueId, FnDefDatum, ImplDatum, - ImplType, OpaqueTyDatum, TraitDatum, WellKnownTrait, + AdtDatum, AssociatedTyDatum, AssociatedTyValue, AssociatedTyValueId, ClosureDatum, FnDefDatum, + ImplDatum, ImplType, OpaqueTyDatum, TraitDatum, WellKnownTrait, }; use chalk_solve::split::Split; use chalk_solve::RustIrDatabase; @@ -412,4 +412,20 @@ impl RustIrDatabase for Program { fn is_object_safe(&self, trait_id: TraitId) -> bool { self.object_safe_traits.contains(&trait_id) } + + fn closure_datum( + &self, + closure_id: ClosureId, + substs: Substitution, + ) -> Arc> { + todo!() + } + + fn closure_upvars( + &self, + closure_id: ClosureId, + substs: Substitution, + ) -> Substitution { + todo!() + } } diff --git a/chalk-ir/src/debug.rs b/chalk-ir/src/debug.rs index 91c1b36a46d..485471f9f9f 100644 --- a/chalk-ir/src/debug.rs +++ b/chalk-ir/src/debug.rs @@ -27,6 +27,12 @@ impl Debug for FnDefId { } } +impl Debug for ClosureId { + fn fmt(&self, fmt: &mut Formatter<'_>) -> std::fmt::Result { + I::debug_closure_id(*self, fmt).unwrap_or_else(|| write!(fmt, "ClosureId({:?})", self.0)) + } +} + impl Debug for Ty { fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error> { I::debug_ty(self, fmt).unwrap_or_else(|| write!(fmt, "{:?}", self.interned)) @@ -169,6 +175,7 @@ impl Debug for TypeName { TypeName::Ref(mutability) => write!(fmt, "{:?}", mutability), TypeName::Never => write!(fmt, "Never"), TypeName::Array => write!(fmt, "{{array}}"), + TypeName::Closure(id) => write!(fmt, "{{closure:{:?}}}", id), TypeName::Error => write!(fmt, "{{error}}"), } } diff --git a/chalk-ir/src/fold/boring_impls.rs b/chalk-ir/src/fold/boring_impls.rs index c190738fdb9..7d14b023498 100644 --- a/chalk-ir/src/fold/boring_impls.rs +++ b/chalk-ir/src/fold/boring_impls.rs @@ -279,6 +279,7 @@ id_fold!(TraitId); id_fold!(AssocTypeId); id_fold!(OpaqueTyId); id_fold!(FnDefId); +id_fold!(ClosureId); impl> SuperFold for ProgramClauseData { fn super_fold_with<'i>( diff --git a/chalk-ir/src/interner.rs b/chalk-ir/src/interner.rs index 1f619ba43b2..eae60aef2c7 100644 --- a/chalk-ir/src/interner.rs +++ b/chalk-ir/src/interner.rs @@ -4,6 +4,7 @@ use crate::ApplicationTy; use crate::AssocTypeId; use crate::CanonicalVarKind; use crate::CanonicalVarKinds; +use crate::ClosureId; use crate::FnDefId; use crate::GenericArg; use crate::GenericArgData; @@ -234,6 +235,14 @@ pub trait Interner: Debug + Copy + Eq + Ord + Hash { None } + #[allow(unused_variables)] + fn debug_closure_id( + fn_def_id: ClosureId, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + None + } + /// Prints the debug representation of an alias. To get good /// results, this requires inspecting TLS, and is difficult to /// code without reference to a specific interner (and hence diff --git a/chalk-ir/src/lib.rs b/chalk-ir/src/lib.rs index 77951c3498b..4b58760ae9a 100644 --- a/chalk-ir/src/lib.rs +++ b/chalk-ir/src/lib.rs @@ -197,6 +197,9 @@ pub enum TypeName { /// the never type `!` Never, + /// A closure. + Closure(ClosureId), + /// This can be used to represent an error, e.g. during name resolution of a type. /// Chalk itself will not produce this, just pass it through when given. Error, @@ -290,6 +293,9 @@ pub struct OpaqueTyId(pub I::DefId); #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct FnDefId(pub I::DefId); +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ClosureId(pub I::DefId); + impl_debugs!(ImplId, ClauseId); #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, HasInterner)] diff --git a/chalk-ir/src/visit/boring_impls.rs b/chalk-ir/src/visit/boring_impls.rs index 1d3cdecb54a..511417b00b0 100644 --- a/chalk-ir/src/visit/boring_impls.rs +++ b/chalk-ir/src/visit/boring_impls.rs @@ -5,10 +5,10 @@ //! The more interesting impls of `Visit` remain in the `visit` module. use crate::{ - AdtId, AssocTypeId, ClausePriority, DebruijnIndex, FloatTy, FnDefId, GenericArg, Goals, ImplId, - IntTy, Interner, Mutability, OpaqueTyId, PlaceholderIndex, ProgramClause, ProgramClauses, - QuantifiedWhereClauses, QuantifierKind, Scalar, Substitution, SuperVisit, TraitId, UintTy, - UniverseIndex, Visit, VisitResult, Visitor, + AdtId, AssocTypeId, ClausePriority, ClosureId, DebruijnIndex, FloatTy, FnDefId, GenericArg, + Goals, ImplId, IntTy, Interner, Mutability, OpaqueTyId, PlaceholderIndex, ProgramClause, + ProgramClauses, QuantifiedWhereClauses, QuantifierKind, Scalar, + Substitution, SuperVisit, TraitId, UintTy, UniverseIndex, Visit, VisitResult, Visitor, }; use std::{marker::PhantomData, sync::Arc}; @@ -235,6 +235,7 @@ id_visit!(TraitId); id_visit!(OpaqueTyId); id_visit!(AssocTypeId); id_visit!(FnDefId); +id_visit!(ClosureId); impl SuperVisit for ProgramClause { fn super_visit_with<'i, R: VisitResult>( diff --git a/chalk-solve/src/clauses.rs b/chalk-solve/src/clauses.rs index 62e7707eedf..73fd7f9c15e 100644 --- a/chalk-solve/src/clauses.rs +++ b/chalk-solve/src/clauses.rs @@ -502,7 +502,8 @@ fn match_type_name( | TypeName::Raw(_) | TypeName::Ref(_) | TypeName::Array - | TypeName::Never => { + | TypeName::Never + | TypeName::Closure(_) => { builder.push_fact(WellFormed::Ty(application.clone().intern(interner))) } } diff --git a/chalk-solve/src/clauses/builtin_traits.rs b/chalk-solve/src/clauses/builtin_traits.rs index 98392bfa083..d88cd6591b0 100644 --- a/chalk-solve/src/clauses/builtin_traits.rs +++ b/chalk-solve/src/clauses/builtin_traits.rs @@ -35,7 +35,7 @@ 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, well_known, self_ty)? } WellKnownTrait::Unsize => { unsize::add_unsize_program_clauses(db, builder, &trait_ref, ty) @@ -57,8 +57,7 @@ pub fn add_builtin_assoc_program_clauses( ) -> 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, well_known, self_ty)?; } _ => {} } diff --git a/chalk-solve/src/clauses/builtin_traits/copy.rs b/chalk-solve/src/clauses/builtin_traits/copy.rs index aec727e31f7..ffd85301cb2 100644 --- a/chalk-solve/src/clauses/builtin_traits/copy.rs +++ b/chalk-solve/src/clauses/builtin_traits/copy.rs @@ -54,6 +54,24 @@ pub fn add_copy_program_clauses( TypeName::FnDef(_) => { builder.push_fact(trait_ref.clone()); } + TypeName::Closure(_) => { + let interner = db.interner(); + match substitution + .parameters(interner) + .last() + .unwrap() + .assert_ty_ref(interner) + .data(interner) + { + TyData::Apply(ApplicationTy { name, substitution }) => match name { + TypeName::Tuple(arity) => { + push_tuple_copy_conditions(db, builder, trait_ref, *arity, substitution) + } + _ => panic!("Expected tuple for upvars."), + }, + _ => panic!("Expected tuple for upvars."), + } + } _ => return, }, TyData::Function(_) => builder.push_fact(trait_ref.clone()), diff --git a/chalk-solve/src/clauses/builtin_traits/fn_family.rs b/chalk-solve/src/clauses/builtin_traits/fn_family.rs index 12c86516ba2..b0df28d0a44 100644 --- a/chalk-solve/src/clauses/builtin_traits/fn_family.rs +++ b/chalk-solve/src/clauses/builtin_traits/fn_family.rs @@ -1,7 +1,8 @@ use crate::clauses::ClauseBuilder; use crate::infer::instantiate::IntoBindersAndValue; -use crate::rust_ir::WellKnownTrait; +use crate::rust_ir::{ClosureKind, WellKnownTrait}; use crate::{Interner, RustIrDatabase, TraitRef}; +use chalk_ir::cast::Cast; use chalk_ir::{ AliasTy, ApplicationTy, Binders, Floundered, Normalize, ProjectionTy, Substitution, TraitId, Ty, TyData, TypeName, VariableKinds, @@ -19,11 +20,116 @@ use chalk_ir::{ pub fn add_fn_trait_program_clauses( db: &dyn RustIrDatabase, builder: &mut ClauseBuilder<'_, I>, - trait_id: TraitId, + well_known: WellKnownTrait, self_ty: Ty, ) -> Result<(), Floundered> { let interner = db.interner(); + let trait_id = db.well_known_trait_id(well_known).unwrap(); match self_ty.data(interner) { + TyData::Apply(apply) => match apply.name { + TypeName::FnDef(fn_def_id) => { + let fn_def_datum = builder.db.fn_def_datum(fn_def_id); + let bound = fn_def_datum + .binders + .substitute(builder.interner(), &apply.substitution); + builder.push_binders(&bound.inputs_and_output, |builder, inputs_and_output| { + let interner = builder.interner(); + let ty = ApplicationTy { + name: apply.name, + substitution: builder.substitution_in_scope(), + } + .intern(interner); + + let substitution = { + let self_ty = ty.cast(interner); + let arguments = ApplicationTy { + name: TypeName::Tuple(inputs_and_output.argument_types.len()), + substitution: Substitution::from( + interner, + inputs_and_output + .argument_types + .iter() + .cloned() + .map(|ty| ty.cast(interner)), + ), + } + .intern(interner) + .cast(interner); + Substitution::from(interner, &[self_ty, arguments]) + }; + builder.push_fact(TraitRef { + trait_id, + substitution: substitution.clone(), + }); + + if let WellKnownTrait::FnOnce = well_known { + let trait_datum = db.trait_datum(trait_id); + let output_id = trait_datum.associated_ty_ids[0]; + let alias = AliasTy::Projection(ProjectionTy { + associated_ty_id: output_id, + substitution, + }); + let return_type = inputs_and_output.return_type; + builder.push_fact(Normalize { + alias, + ty: return_type, + }); + } + }); + Ok(()) + } + TypeName::Closure(closure_id) => { + let closure_datum = db.closure_datum(closure_id, apply.substitution.clone()); + let trait_matches = match well_known { + WellKnownTrait::Fn => matches!(closure_datum.kind, ClosureKind::Fn), + WellKnownTrait::FnMut => { + matches!(closure_datum.kind, ClosureKind::FnMut | ClosureKind::Fn) + } + WellKnownTrait::FnOnce => matches!( + closure_datum.kind, + ClosureKind::FnOnce | ClosureKind::FnMut | ClosureKind::Fn + ), + _ => false, + }; + if trait_matches { + builder.push_binders( + &closure_datum.inputs_and_output, + |builder, inputs_and_output| { + let substitution = Substitution::from( + interner, + Some(self_ty.cast(interner)).into_iter().chain( + inputs_and_output + .argument_types + .iter() + .cloned() + .map(|ty| ty.cast(interner)), + ), + ); + builder.push_fact(TraitRef { + trait_id, + substitution: substitution.clone(), + }); + + if let WellKnownTrait::FnOnce = well_known { + let trait_datum = db.trait_datum(trait_id); + let output_id = trait_datum.associated_ty_ids[0]; + let alias = AliasTy::Projection(ProjectionTy { + associated_ty_id: output_id, + substitution, + }); + let return_type = inputs_and_output.return_type.clone(); + builder.push_fact(Normalize { + alias, + ty: return_type, + }); + } + }, + ); + } + Ok(()) + } + _ => Ok(()), + }, TyData::Function(fn_val) => { let (binders, orig_sub) = fn_val.into_binders_and_value(interner); let bound_ref = Binders::new(VariableKinds::from(interner, binders), orig_sub); diff --git a/chalk-solve/src/clauses/builtin_traits/sized.rs b/chalk-solve/src/clauses/builtin_traits/sized.rs index 3df7c2fb64a..fc5a525bc77 100644 --- a/chalk-solve/src/clauses/builtin_traits/sized.rs +++ b/chalk-solve/src/clauses/builtin_traits/sized.rs @@ -76,6 +76,7 @@ pub fn add_sized_program_clauses( } TypeName::Array | TypeName::Never + | TypeName::Closure(_) | TypeName::FnDef(_) | TypeName::Scalar(_) | TypeName::Raw(_) diff --git a/chalk-solve/src/lib.rs b/chalk-solve/src/lib.rs index 4e9f86dd738..85285856a44 100644 --- a/chalk-solve/src/lib.rs +++ b/chalk-solve/src/lib.rs @@ -101,6 +101,14 @@ pub trait RustIrDatabase: Debug { /// Check if a trait is object safe fn is_object_safe(&self, trait_id: TraitId) -> bool; + + fn closure_datum( + &self, + closure_id: ClosureId, + substs: Substitution, + ) -> Arc>; + + fn closure_upvars(&self, closure_id: ClosureId, substs: Substitution) -> Substitution; } pub use clauses::program_clauses_for_env; diff --git a/chalk-solve/src/rust_ir.rs b/chalk-solve/src/rust_ir.rs index 95db243c562..f0ee22e049c 100644 --- a/chalk-solve/src/rust_ir.rs +++ b/chalk-solve/src/rust_ir.rs @@ -168,6 +168,12 @@ pub struct FnDefDatumBound { pub where_clauses: Vec>, } +pub struct ClosureDatum { + pub inputs_and_output: Binders>, + + pub kind: ClosureKind, +} + #[derive(Clone, Debug, PartialEq, Eq, Hash)] /// A rust intermediate representation (rust_ir) of a Trait Definition. For /// example, given the following rust code: @@ -591,3 +597,10 @@ impl Polarity { } } } + +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] +pub enum ClosureKind { + Fn, + FnMut, + FnOnce, +} diff --git a/tests/integration/panic.rs b/tests/integration/panic.rs index d6a3cc2ef23..44aa2d38458 100644 --- a/tests/integration/panic.rs +++ b/tests/integration/panic.rs @@ -185,6 +185,22 @@ impl RustIrDatabase for MockDatabase { fn is_object_safe(&self, trait_id: TraitId) -> bool { unimplemented!() } + + fn closure_datum( + &self, + closure_id: ClosureId, + substs: Substitution, + ) -> Arc> { + unimplemented!() + } + + fn closure_upvars( + &self, + closure_id: ClosureId, + substs: Substitution, + ) -> Substitution { + unimplemented!() + } } fn prepare_goal() -> UCanonical>> { diff --git a/tests/test/fn_def.rs b/tests/test/fn_def.rs index 9dbdf588fb6..72e61fcea12 100644 --- a/tests/test/fn_def.rs +++ b/tests/test/fn_def.rs @@ -64,3 +64,37 @@ fn fn_def_is_clone() { } } } + +#[test] +fn fn_def_implements_fn_traits() { + test! { + program { + #[lang(fn_once)] + trait FnOnce { + type Output; + } + + #[lang(fn_mut)] + trait FnMut where Self: FnOnce { } + + #[lang(fn)] + trait Fn where Self: FnMut { } + + fn foo(); + fn bar(one: i32); + fn baz(one: i32) -> u8; + } + + goal { + foo: Fn<()> + } yields { + "Unique" + } + + goal { + Normalize(>::Output -> ()) + } yields { + "Unique" + } + } +} From 2ab14fc28101f1a2d26de4f3ef69fb839e5d9696 Mon Sep 17 00:00:00 2001 From: Jack Huey Date: Sat, 13 Jun 2020 00:05:35 -0400 Subject: [PATCH 2/7] Review comments --- .../src/clauses/builtin_traits/copy.rs | 14 +- .../src/clauses/builtin_traits/fn_family.rs | 201 +++++++----------- 2 files changed, 76 insertions(+), 139 deletions(-) diff --git a/chalk-solve/src/clauses/builtin_traits/copy.rs b/chalk-solve/src/clauses/builtin_traits/copy.rs index ffd85301cb2..70dd46a39dd 100644 --- a/chalk-solve/src/clauses/builtin_traits/copy.rs +++ b/chalk-solve/src/clauses/builtin_traits/copy.rs @@ -56,21 +56,13 @@ pub fn add_copy_program_clauses( } TypeName::Closure(_) => { let interner = db.interner(); - match substitution + let last_ty = substitution .parameters(interner) .last() .unwrap() .assert_ty_ref(interner) - .data(interner) - { - TyData::Apply(ApplicationTy { name, substitution }) => match name { - TypeName::Tuple(arity) => { - push_tuple_copy_conditions(db, builder, trait_ref, *arity, substitution) - } - _ => panic!("Expected tuple for upvars."), - }, - _ => panic!("Expected tuple for upvars."), - } + .clone(); + needs_impl_for_tys(db, builder, trait_ref, Some(last_ty).into_iter()); } _ => return, }, diff --git a/chalk-solve/src/clauses/builtin_traits/fn_family.rs b/chalk-solve/src/clauses/builtin_traits/fn_family.rs index b0df28d0a44..b9f9111c0a6 100644 --- a/chalk-solve/src/clauses/builtin_traits/fn_family.rs +++ b/chalk-solve/src/clauses/builtin_traits/fn_family.rs @@ -4,8 +4,8 @@ use crate::rust_ir::{ClosureKind, WellKnownTrait}; use crate::{Interner, RustIrDatabase, TraitRef}; use chalk_ir::cast::Cast; use chalk_ir::{ - AliasTy, ApplicationTy, Binders, Floundered, Normalize, ProjectionTy, Substitution, TraitId, - Ty, TyData, TypeName, VariableKinds, + AliasTy, ApplicationTy, Binders, Floundered, Normalize, ProjectionTy, Substitution, Ty, TyData, + TypeName, VariableKinds, }; /// Handles clauses for FnOnce/FnMut/Fn. @@ -25,6 +25,45 @@ pub fn add_fn_trait_program_clauses( ) -> Result<(), Floundered> { let interner = db.interner(); let trait_id = db.well_known_trait_id(well_known).unwrap(); + + let push_clauses = |builder: &mut ClauseBuilder<'_, I>, + self_ty: Ty, + arg_sub: Substitution, + return_type| { + let tupled = TyData::Apply(ApplicationTy { + name: TypeName::Tuple(arg_sub.len(interner)), + substitution: arg_sub, + }) + .intern(interner); + let substitution = + Substitution::from(interner, &[self_ty.cast(interner), tupled.cast(interner)]); + builder.push_fact(TraitRef { + trait_id, + substitution: substitution.clone(), + }); + + // The `Output` type is defined on the `FnOnce` + if let WellKnownTrait::FnOnce = well_known { + let trait_datum = db.trait_datum(trait_id); + assert_eq!( + trait_datum.associated_ty_ids.len(), + 1, + "FnOnce trait should have exactly one associated type, found {:?}", + trait_datum.associated_ty_ids + ); + // Constructs the alias. For `Fn`, for example, this would look like + // `Normalize( B as FnOnce<(A,)>>::Output -> B)` + let output_id = trait_datum.associated_ty_ids[0]; + let alias = AliasTy::Projection(ProjectionTy { + associated_ty_id: output_id, + substitution, + }); + builder.push_fact(Normalize { + alias, + ty: return_type, + }); + } + }; match self_ty.data(interner) { TyData::Apply(apply) => match apply.name { TypeName::FnDef(fn_def_id) => { @@ -32,100 +71,50 @@ pub fn add_fn_trait_program_clauses( let bound = fn_def_datum .binders .substitute(builder.interner(), &apply.substitution); - builder.push_binders(&bound.inputs_and_output, |builder, inputs_and_output| { - let interner = builder.interner(); - let ty = ApplicationTy { + let inputs_and_output = &bound.inputs_and_output; + builder.push_binders(inputs_and_output, |builder, inputs_and_output| { + let self_ty = ApplicationTy { name: apply.name, substitution: builder.substitution_in_scope(), } .intern(interner); - let substitution = { - let self_ty = ty.cast(interner); - let arguments = ApplicationTy { - name: TypeName::Tuple(inputs_and_output.argument_types.len()), - substitution: Substitution::from( - interner, - inputs_and_output - .argument_types - .iter() - .cloned() - .map(|ty| ty.cast(interner)), - ), - } - .intern(interner) - .cast(interner); - Substitution::from(interner, &[self_ty, arguments]) - }; - builder.push_fact(TraitRef { - trait_id, - substitution: substitution.clone(), - }); + let arg_sub = inputs_and_output + .argument_types + .iter() + .cloned() + .map(|ty| ty.cast(interner)); + let arg_sub = Substitution::from(interner, arg_sub); + let output_ty = inputs_and_output.return_type; - if let WellKnownTrait::FnOnce = well_known { - let trait_datum = db.trait_datum(trait_id); - let output_id = trait_datum.associated_ty_ids[0]; - let alias = AliasTy::Projection(ProjectionTy { - associated_ty_id: output_id, - substitution, - }); - let return_type = inputs_and_output.return_type; - builder.push_fact(Normalize { - alias, - ty: return_type, - }); - } + push_clauses(builder, self_ty, arg_sub, output_ty); }); Ok(()) } TypeName::Closure(closure_id) => { let closure_datum = db.closure_datum(closure_id, apply.substitution.clone()); - let trait_matches = match well_known { - WellKnownTrait::Fn => matches!(closure_datum.kind, ClosureKind::Fn), - WellKnownTrait::FnMut => { - matches!(closure_datum.kind, ClosureKind::FnMut | ClosureKind::Fn) - } - WellKnownTrait::FnOnce => matches!( - closure_datum.kind, - ClosureKind::FnOnce | ClosureKind::FnMut | ClosureKind::Fn - ), + let trait_matches = match (well_known, closure_datum.kind) { + (WellKnownTrait::Fn, ClosureKind::Fn) => true, + (WellKnownTrait::FnMut, ClosureKind::FnMut) + | (WellKnownTrait::FnMut, ClosureKind::Fn) => true, + (WellKnownTrait::FnOnce, _) => true, _ => false, }; - if trait_matches { - builder.push_binders( - &closure_datum.inputs_and_output, - |builder, inputs_and_output| { - let substitution = Substitution::from( - interner, - Some(self_ty.cast(interner)).into_iter().chain( - inputs_and_output - .argument_types - .iter() - .cloned() - .map(|ty| ty.cast(interner)), - ), - ); - builder.push_fact(TraitRef { - trait_id, - substitution: substitution.clone(), - }); - - if let WellKnownTrait::FnOnce = well_known { - let trait_datum = db.trait_datum(trait_id); - let output_id = trait_datum.associated_ty_ids[0]; - let alias = AliasTy::Projection(ProjectionTy { - associated_ty_id: output_id, - substitution, - }); - let return_type = inputs_and_output.return_type.clone(); - builder.push_fact(Normalize { - alias, - ty: return_type, - }); - } - }, - ); + if !trait_matches { + return Ok(()); } + let inputs_and_output = &closure_datum.inputs_and_output; + builder.push_binders(inputs_and_output, |builder, inputs_and_output| { + let arg_sub = inputs_and_output + .argument_types + .iter() + .cloned() + .map(|ty| ty.cast(interner)); + let arg_sub = Substitution::from(interner, arg_sub); + let output_ty = inputs_and_output.return_type; + + push_clauses(builder, self_ty, arg_sub, output_ty); + }); Ok(()) } _ => Ok(()), @@ -139,53 +128,9 @@ pub fn add_fn_trait_program_clauses( .parameters(interner) .split_at(orig_sub.len(interner) - 1); let arg_sub = Substitution::from(interner, arg_sub); - let fn_output_ty = fn_output_ty[0].assert_ty_ref(interner); - - // We are constructing a reference to `FnOnce`, where - // `Args` is a tuple of the function's argument types - let tupled = Ty::new( - interner, - TyData::Apply(ApplicationTy { - name: TypeName::Tuple(arg_sub.len(interner)), - substitution: arg_sub.clone(), - }), - ); - - let tupled_sub = Substitution::from(interner, vec![self_ty.clone(), tupled]); - // Given a function type `fn(A1, A2, ..., AN)`, construct a `TraitRef` - // of the form `fn(A1, A2, ..., AN): FnOnce<(A1, A2, ..., AN)>` - let new_trait_ref = TraitRef { - trait_id, - substitution: tupled_sub.clone(), - }; + let output_ty = fn_output_ty[0].assert_ty_ref(interner).clone(); - builder.push_fact(new_trait_ref.clone()); - - if let Some(WellKnownTrait::FnOnce) = db.trait_datum(trait_id).well_known { - //The `Output` type is defined on the `FnOnce` - let fn_once = db.trait_datum(trait_id); - assert_eq!(fn_once.well_known, Some(WellKnownTrait::FnOnce)); - let assoc_types = &fn_once.associated_ty_ids; - assert_eq!( - assoc_types.len(), - 1, - "FnOnce trait should have exactly one associated type, found {:?}", - assoc_types - ); - - // Construct `Normalize( B as FnOnce<(A,)>>::Output -> B)` - let assoc_output_ty = assoc_types[0]; - let proj_ty = ProjectionTy { - associated_ty_id: assoc_output_ty, - substitution: tupled_sub, - }; - let normalize = Normalize { - alias: AliasTy::Projection(proj_ty), - ty: fn_output_ty.clone(), - }; - - builder.push_fact(normalize); - } + push_clauses(builder, self_ty.clone(), arg_sub, output_ty); }); Ok(()) } From 35f596741e40d688db3164af74d7885be2e6d022 Mon Sep 17 00:00:00 2001 From: Jack Huey Date: Sat, 13 Jun 2020 02:05:53 -0400 Subject: [PATCH 3/7] Initital set of tests for closures --- chalk-integration/src/db.rs | 6 +- chalk-integration/src/lowering.rs | 88 +++++++- chalk-integration/src/program.rs | 19 +- chalk-parse/src/ast.rs | 16 ++ chalk-parse/src/parser.lalrpop | 16 ++ .../src/clauses/builtin_traits/copy.rs | 12 +- .../src/clauses/builtin_traits/fn_family.rs | 2 +- chalk-solve/src/lib.rs | 4 +- chalk-solve/src/rust_ir.rs | 9 +- tests/integration/panic.rs | 6 +- tests/lowering/mod.rs | 11 + tests/test/closures.rs | 190 ++++++++++++++++++ tests/test/mod.rs | 1 + 13 files changed, 352 insertions(+), 28 deletions(-) create mode 100644 tests/test/closures.rs diff --git a/chalk-integration/src/db.rs b/chalk-integration/src/db.rs index 5bf78b1e037..eacb85206b1 100644 --- a/chalk-integration/src/db.rs +++ b/chalk-integration/src/db.rs @@ -159,7 +159,7 @@ impl RustIrDatabase for ChalkDatabase { fn closure_datum( &self, closure_id: ClosureId, - substs: Substitution, + substs: &Substitution, ) -> Arc> { self.program_ir().unwrap().closure_datum(closure_id, substs) } @@ -167,8 +167,8 @@ impl RustIrDatabase for ChalkDatabase { fn closure_upvars( &self, closure_id: ClosureId, - substs: Substitution, - ) -> Substitution { + substs: &Substitution, + ) -> Ty { self.program_ir() .unwrap() .closure_upvars(closure_id, substs) diff --git a/chalk-integration/src/lowering.rs b/chalk-integration/src/lowering.rs index 8189ae54018..7a88e2388be 100644 --- a/chalk-integration/src/lowering.rs +++ b/chalk-integration/src/lowering.rs @@ -2,8 +2,8 @@ use crate::interner::{ChalkFnAbi, ChalkIr}; use chalk_ir::cast::{Cast, Caster}; use chalk_ir::interner::{HasInterner, Interner}; use chalk_ir::{ - self, AdtId, AssocTypeId, BoundVar, ClausePriority, DebruijnIndex, FnDefId, ImplId, OpaqueTyId, - QuantifiedWhereClauses, Substitution, ToGenericArg, TraitId, TyKind, + self, AdtId, AssocTypeId, BoundVar, ClausePriority, ClosureId, DebruijnIndex, FnDefId, ImplId, + OpaqueTyId, QuantifiedWhereClauses, Substitution, ToGenericArg, TraitId, TyKind, }; use chalk_ir::{debug, debug_heading}; use chalk_parse::ast::*; @@ -20,6 +20,7 @@ use crate::{Identifier as Ident, RawId, TypeKind, TypeSort}; type AdtIds = BTreeMap>; type FnDefIds = BTreeMap>; +type ClosureIds = BTreeMap>; type TraitIds = BTreeMap>; type OpaqueTyIds = BTreeMap>; type AdtKinds = BTreeMap, TypeKind>; @@ -42,6 +43,7 @@ struct Env<'k> { fn_def_ids: &'k FnDefIds, fn_def_kinds: &'k FnDefKinds, fn_def_abis: &'k FnDefAbis, + closure_ids: &'k ClosureIds, trait_ids: &'k TraitIds, trait_kinds: &'k TraitKinds, opaque_ty_ids: &'k OpaqueTyIds, @@ -141,6 +143,26 @@ impl<'k> Env<'k> { } } + if let Some(id) = self.closure_ids.get(&name.str) { + return Ok(chalk_ir::TyData::Apply(chalk_ir::ApplicationTy { + name: chalk_ir::TypeName::Closure(id.clone()), + // XXX closure upvars + substitution: chalk_ir::Substitution::from( + interner, + Some( + chalk_ir::TyData::Apply(chalk_ir::ApplicationTy { + name: chalk_ir::TypeName::Tuple(0), + substitution: chalk_ir::Substitution::empty(interner), + }) + .intern(interner) + .cast(interner), + ), + ), + }) + .intern(interner) + .cast(interner)); + } + if let Some(id) = self.opaque_ty_ids.get(&name.str) { return Ok( chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { @@ -318,6 +340,7 @@ impl LowerProgram for Program { let mut adt_ids = BTreeMap::new(); let mut fn_def_ids = BTreeMap::new(); + let mut closure_ids = BTreeMap::new(); let mut trait_ids = BTreeMap::new(); let mut opaque_ty_ids = BTreeMap::new(); let mut adt_kinds = BTreeMap::new(); @@ -341,6 +364,10 @@ impl LowerProgram for Program { fn_def_kinds.insert(id, type_kind); fn_def_abis.insert(id, defn.abi.lower()?); } + Item::ClosureDefn(defn) => { + let id = ClosureId(raw_id); + closure_ids.insert(defn.name.str.clone(), id); + } Item::TraitDefn(defn) => { let type_kind = defn.lower_type_kind()?; let id = TraitId(raw_id); @@ -364,6 +391,7 @@ impl LowerProgram for Program { let mut adt_data = BTreeMap::new(); let mut fn_def_data = BTreeMap::new(); + let mut closure_data = BTreeMap::new(); let mut trait_data = BTreeMap::new(); let mut well_known_traits = BTreeMap::new(); let mut impl_data = BTreeMap::new(); @@ -379,6 +407,7 @@ impl LowerProgram for Program { fn_def_ids: &fn_def_ids, fn_def_kinds: &fn_def_kinds, fn_def_abis: &fn_def_abis, + closure_ids: &closure_ids, trait_ids: &trait_ids, trait_kinds: &trait_kinds, opaque_ty_ids: &opaque_ty_ids, @@ -399,6 +428,13 @@ impl LowerProgram for Program { Arc::new(defn.lower_fn_def(fn_def_id, &empty_env)?), ); } + Item::ClosureDefn(ref defn) => { + let closure_def_id = ClosureId(raw_id); + closure_data.insert( + closure_def_id, + Arc::new(defn.lower_closure(closure_def_id, &empty_env)?), + ); + } Item::TraitDefn(ref trait_defn) => { let trait_id = TraitId(raw_id); let trait_datum = trait_defn.lower_trait(trait_id, &empty_env)?; @@ -556,12 +592,14 @@ impl LowerProgram for Program { let program = LoweredProgram { adt_ids, fn_def_ids, + closure_ids, trait_ids, adt_kinds, fn_def_kinds, trait_kinds, adt_data, fn_def_data, + closure_data, trait_data, well_known_traits, impl_data, @@ -1071,6 +1109,51 @@ impl LowerFnAbi for FnAbi { } } +trait LowerClosureDefn { + fn lower_closure( + &self, + closure_id: chalk_ir::ClosureId, + env: &Env, + ) -> LowerResult>; +} + +impl LowerClosureDefn for ClosureDefn { + fn lower_closure( + &self, + closure_id: chalk_ir::ClosureId, + env: &Env, + ) -> LowerResult> { + let inputs_and_output = env.in_binders(vec![], |env| { + let args: LowerResult<_> = self.argument_types.iter().map(|t| t.lower(env)).collect(); + let return_type = self.return_type.lower(env)?; + Ok(rust_ir::FnDefInputsAndOutputDatum { + argument_types: args?, + return_type, + }) + })?; + + Ok(rust_ir::ClosureDatum { + id: closure_id, + kind: self.kind.lower_closure_kind()?, + inputs_and_output, + }) + } +} + +trait LowerClosureKind { + fn lower_closure_kind(&self) -> LowerResult; +} + +impl LowerClosureKind for ClosureKind { + fn lower_closure_kind(&self) -> LowerResult { + Ok(match self { + ClosureKind::Fn => rust_ir::ClosureKind::Fn, + ClosureKind::FnMut => rust_ir::ClosureKind::FnMut, + ClosureKind::FnOnce => rust_ir::ClosureKind::FnOnce, + }) + } +} + trait LowerTraitRef { fn lower(&self, env: &Env) -> LowerResult>; } @@ -1763,6 +1846,7 @@ impl LowerGoal for Goal { let env = Env { adt_ids: &program.adt_ids, fn_def_ids: &program.fn_def_ids, + closure_ids: &program.closure_ids, trait_ids: &program.trait_ids, opaque_ty_ids: &program.opaque_ty_ids, adt_kinds: &program.adt_kinds, diff --git a/chalk-integration/src/program.rs b/chalk-integration/src/program.rs index 7988a2dfd3f..155b96bfa9a 100644 --- a/chalk-integration/src/program.rs +++ b/chalk-integration/src/program.rs @@ -29,6 +29,8 @@ pub struct Program { pub fn_def_kinds: BTreeMap, TypeKind>, + pub closure_ids: BTreeMap>, + /// From trait name to item-id. Used during lowering only. pub trait_ids: BTreeMap>, @@ -40,6 +42,8 @@ pub struct Program { pub fn_def_data: BTreeMap, Arc>>, + pub closure_data: BTreeMap, Arc>>, + /// For each impl: pub impl_data: BTreeMap, Arc>>, @@ -416,16 +420,21 @@ impl RustIrDatabase for Program { fn closure_datum( &self, closure_id: ClosureId, - substs: Substitution, + substs: &Substitution, ) -> Arc> { - todo!() + self.closure_data[&closure_id].clone() } fn closure_upvars( &self, closure_id: ClosureId, - substs: Substitution, - ) -> Substitution { - todo!() + substs: &Substitution, + ) -> Ty { + substs + .parameters(self.interner()) + .last() + .unwrap() + .assert_ty_ref(self.interner()) + .clone() } } diff --git a/chalk-parse/src/ast.rs b/chalk-parse/src/ast.rs index cc3682473f2..867816608df 100644 --- a/chalk-parse/src/ast.rs +++ b/chalk-parse/src/ast.rs @@ -22,6 +22,7 @@ pub struct Program { pub enum Item { StructDefn(StructDefn), FnDefn(FnDefn), + ClosureDefn(ClosureDefn), TraitDefn(TraitDefn), OpaqueTyDefn(OpaqueTyDefn), Impl(Impl), @@ -54,6 +55,14 @@ pub struct FnDefn { pub abi: FnAbi, } +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct ClosureDefn { + pub name: Identifier, + pub kind: ClosureKind, + pub argument_types: Vec, + pub return_type: Ty, +} + #[derive(Clone, Eq, PartialEq, Debug)] pub struct FnAbi(pub Atom); @@ -409,3 +418,10 @@ pub enum Goal { // Additional kinds of goals: Leaf(LeafGoal), } + +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum ClosureKind { + Fn, + FnMut, + FnOnce, +} diff --git a/chalk-parse/src/parser.lalrpop b/chalk-parse/src/parser.lalrpop index 7b63a01f496..c776d1ff952 100644 --- a/chalk-parse/src/parser.lalrpop +++ b/chalk-parse/src/parser.lalrpop @@ -15,6 +15,7 @@ Item: Option = { Comment => None, StructDefn => Some(Item::StructDefn(<>)), FnDefn => Some(Item::FnDefn(<>)), + ClosureDefn => Some(Item::ClosureDefn(<>)), TraitDefn => Some(Item::TraitDefn(<>)), OpaqueTyDefn => Some(Item::OpaqueTyDefn(<>)), Impl => Some(Item::Impl(<>)), @@ -102,6 +103,21 @@ FnArgs: Vec = { > }; +ClosureDefn: ClosureDefn = { + "closure" "(" ")" ";" => ClosureDefn { + name: n, + kind: s, + argument_types: vec![], + return_type: Ty::Tuple { types: Vec::new() }, + } +} + +ClosureSelf: ClosureKind = { + "self" => ClosureKind::FnOnce, + "mutself" => ClosureKind::FnMut, + "refself" => ClosureKind::Fn, +} + TraitDefn: TraitDefn = { "trait" > "{" "}" => TraitDefn diff --git a/chalk-solve/src/clauses/builtin_traits/copy.rs b/chalk-solve/src/clauses/builtin_traits/copy.rs index 70dd46a39dd..5bb2fa9023f 100644 --- a/chalk-solve/src/clauses/builtin_traits/copy.rs +++ b/chalk-solve/src/clauses/builtin_traits/copy.rs @@ -54,15 +54,9 @@ pub fn add_copy_program_clauses( TypeName::FnDef(_) => { builder.push_fact(trait_ref.clone()); } - TypeName::Closure(_) => { - let interner = db.interner(); - let last_ty = substitution - .parameters(interner) - .last() - .unwrap() - .assert_ty_ref(interner) - .clone(); - needs_impl_for_tys(db, builder, trait_ref, Some(last_ty).into_iter()); + TypeName::Closure(closure_id) => { + let upvars = db.closure_upvars(*closure_id, substitution); + needs_impl_for_tys(db, builder, trait_ref, Some(upvars).into_iter()); } _ => return, }, diff --git a/chalk-solve/src/clauses/builtin_traits/fn_family.rs b/chalk-solve/src/clauses/builtin_traits/fn_family.rs index b9f9111c0a6..a502c62097e 100644 --- a/chalk-solve/src/clauses/builtin_traits/fn_family.rs +++ b/chalk-solve/src/clauses/builtin_traits/fn_family.rs @@ -92,7 +92,7 @@ pub fn add_fn_trait_program_clauses( Ok(()) } TypeName::Closure(closure_id) => { - let closure_datum = db.closure_datum(closure_id, apply.substitution.clone()); + let closure_datum = db.closure_datum(closure_id, &apply.substitution); let trait_matches = match (well_known, closure_datum.kind) { (WellKnownTrait::Fn, ClosureKind::Fn) => true, (WellKnownTrait::FnMut, ClosureKind::FnMut) diff --git a/chalk-solve/src/lib.rs b/chalk-solve/src/lib.rs index 85285856a44..83bc606c91b 100644 --- a/chalk-solve/src/lib.rs +++ b/chalk-solve/src/lib.rs @@ -105,10 +105,10 @@ pub trait RustIrDatabase: Debug { fn closure_datum( &self, closure_id: ClosureId, - substs: Substitution, + substs: &Substitution, ) -> Arc>; - fn closure_upvars(&self, closure_id: ClosureId, substs: Substitution) -> Substitution; + fn closure_upvars(&self, closure_id: ClosureId, substs: &Substitution) -> Ty; } pub use clauses::program_clauses_for_env; diff --git a/chalk-solve/src/rust_ir.rs b/chalk-solve/src/rust_ir.rs index f0ee22e049c..c1ee0b7dc63 100644 --- a/chalk-solve/src/rust_ir.rs +++ b/chalk-solve/src/rust_ir.rs @@ -9,9 +9,9 @@ use chalk_ir::cast::Cast; use chalk_ir::fold::shift::Shift; use chalk_ir::interner::{Interner, TargetInterner}; use chalk_ir::{ - AdtId, AliasEq, AliasTy, AssocTypeId, Binders, DebruijnIndex, FnDefId, GenericArg, ImplId, - OpaqueTyId, ProjectionTy, QuantifiedWhereClause, Substitution, ToGenericArg, TraitId, TraitRef, - Ty, TyData, TypeName, VariableKind, WhereClause, WithKind, + AdtId, AliasEq, AliasTy, AssocTypeId, Binders, ClosureId, DebruijnIndex, FnDefId, GenericArg, + ImplId, OpaqueTyId, ProjectionTy, QuantifiedWhereClause, Substitution, ToGenericArg, TraitId, + TraitRef, Ty, TyData, TypeName, VariableKind, WhereClause, WithKind, }; use std::iter; @@ -168,7 +168,10 @@ pub struct FnDefDatumBound { pub where_clauses: Vec>, } +#[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct ClosureDatum { + pub id: ClosureId, + pub inputs_and_output: Binders>, pub kind: ClosureKind, diff --git a/tests/integration/panic.rs b/tests/integration/panic.rs index 44aa2d38458..7173e37fa0f 100644 --- a/tests/integration/panic.rs +++ b/tests/integration/panic.rs @@ -189,7 +189,7 @@ impl RustIrDatabase for MockDatabase { fn closure_datum( &self, closure_id: ClosureId, - substs: Substitution, + substs: &Substitution, ) -> Arc> { unimplemented!() } @@ -197,8 +197,8 @@ impl RustIrDatabase for MockDatabase { fn closure_upvars( &self, closure_id: ClosureId, - substs: Substitution, - ) -> Substitution { + substs: &Substitution, + ) -> Ty { unimplemented!() } } diff --git a/tests/lowering/mod.rs b/tests/lowering/mod.rs index 780e0b9fd80..37ac8ab15e1 100644 --- a/tests/lowering/mod.rs +++ b/tests/lowering/mod.rs @@ -717,3 +717,14 @@ fn extern_functions() { } } } + +#[test] +fn closures() { + lowering_success! { + program { + closure foo(self); + closure bar(refself); + closure baz(mutself); + } + } +} diff --git a/tests/test/closures.rs b/tests/test/closures.rs new file mode 100644 index 00000000000..7e7ba4a1585 --- /dev/null +++ b/tests/test/closures.rs @@ -0,0 +1,190 @@ +use super::*; + +#[test] +fn closure_is_well_formed() { + test! { + program { + closure foo(self); + closure bar(refself); + closure baz(mutself); + } + + goal { + WellFormed(foo) + } yields { + "Unique" + } + goal { + WellFormed(bar) + } yields { + "Unique" + } + goal { + WellFormed(baz) + } yields { + "Unique" + } + } +} + +#[test] +fn closure_is_sized() { + test! { + program { + #[lang(sized)] + trait Sized { } + + closure foo(self); + } + + goal { + foo: Sized + } yields { + "Unique" + } + } +} + +#[test] +fn closure_is_copy() { + test! { + program { + #[lang(copy)] + trait Copy { } + + closure foo(self); + closure bar(refself); + closure baz(mutself); + } + + goal { + foo: Copy + } yields { + "Unique" + } + goal { + bar: Copy + } yields { + "Unique" + } + goal { + baz: Copy + } yields { + "Unique" + } + } +} + +#[test] +fn closure_is_clone() { + test! { + program { + #[lang(clone)] + trait Clone { } + + closure foo(self); + closure bar(refself); + closure baz(mutself); + } + goal { + foo: Clone + } yields { + "Unique" + } + goal { + bar: Clone + } yields { + "Unique" + } + goal { + baz: Clone + } yields { + "Unique" + } + } +} + +#[test] +fn closure_implements_fn_traits() { + test! { + program { + #[lang(fn_once)] + trait FnOnce { + type Output; + } + + #[lang(fn_mut)] + trait FnMut where Self: FnOnce { } + + #[lang(fn)] + trait Fn where Self: FnMut { } + + closure foo(self); + closure bar(refself); + closure baz(mutself); + } + + goal { + foo: Fn<()> + } yields { + "No possible solution" + } + goal { + foo: FnMut<()> + } yields { + "No possible solution" + } + goal { + foo: FnOnce<()> + } yields { + "Unique" + } + goal { + Normalize(>::Output -> ()) + } yields { + "Unique" + } + + goal { + bar: Fn<()> + } yields { + "Unique" + } + goal { + bar: FnMut<()> + } yields { + "Unique" + } + goal { + bar: FnOnce<()> + } yields { + "Unique" + } + goal { + Normalize(>::Output -> ()) + } yields { + "Unique" + } + + goal { + baz: Fn<()> + } yields { + "No possible solution" + } + goal { + baz: FnMut<()> + } yields { + "Unique" + } + goal { + baz: FnOnce<()> + } yields { + "Unique" + } + goal { + Normalize(>::Output -> ()) + } yields { + "Unique" + } + } +} diff --git a/tests/test/mod.rs b/tests/test/mod.rs index d91afc0201c..b957e59a5a2 100644 --- a/tests/test/mod.rs +++ b/tests/test/mod.rs @@ -299,6 +299,7 @@ fn solve_goal(program_text: &str, goals: Vec<(&str, SolverChoice, TestGoal)>) { mod arrays; mod auto_traits; mod builtin_impls; +mod closures; mod coherence_goals; mod coinduction; mod constants; From a4072ff5436264257d4d6d4b86cbde9d7a1d85b6 Mon Sep 17 00:00:00 2001 From: Jack Huey Date: Tue, 16 Jun 2020 00:49:11 -0400 Subject: [PATCH 4/7] More closures work --- chalk-integration/src/db.rs | 8 +- chalk-integration/src/lib.rs | 1 + chalk-integration/src/lowering.rs | 76 ++++++++++--- chalk-integration/src/program.rs | 25 +++-- chalk-parse/src/ast.rs | 2 + chalk-parse/src/parser.lalrpop | 17 ++- .../src/clauses/builtin_traits/copy.rs | 4 +- .../src/clauses/builtin_traits/fn_family.rs | 105 +++++++++++------- chalk-solve/src/lib.rs | 2 +- tests/integration/panic.rs | 2 +- tests/lowering/mod.rs | 15 ++- tests/test/closures.rs | 95 +++++++++++++--- 12 files changed, 257 insertions(+), 95 deletions(-) diff --git a/chalk-integration/src/db.rs b/chalk-integration/src/db.rs index eacb85206b1..f79d5bf791c 100644 --- a/chalk-integration/src/db.rs +++ b/chalk-integration/src/db.rs @@ -7,9 +7,9 @@ use crate::{ tls, }; use chalk_ir::{ - AdtId, AssocTypeId, Canonical, ClosureId, ConstrainedSubst, Environment, FnDefId, GenericArg, - Goal, ImplId, InEnvironment, OpaqueTyId, ProgramClause, ProgramClauses, Substitution, TraitId, - Ty, UCanonical, + AdtId, AssocTypeId, Binders, Canonical, ClosureId, ConstrainedSubst, Environment, FnDefId, + GenericArg, Goal, ImplId, InEnvironment, OpaqueTyId, ProgramClause, ProgramClauses, + Substitution, TraitId, Ty, UCanonical, }; use chalk_solve::rust_ir::{ AdtDatum, AssociatedTyDatum, AssociatedTyValue, AssociatedTyValueId, ClosureDatum, FnDefDatum, @@ -168,7 +168,7 @@ impl RustIrDatabase for ChalkDatabase { &self, closure_id: ClosureId, substs: &Substitution, - ) -> Ty { + ) -> Binders> { self.program_ir() .unwrap() .closure_upvars(closure_id, substs) diff --git a/chalk-integration/src/lib.rs b/chalk-integration/src/lib.rs index d3d21a7e0a3..1019f601567 100644 --- a/chalk-integration/src/lib.rs +++ b/chalk-integration/src/lib.rs @@ -20,6 +20,7 @@ pub use interner::{Identifier, RawId}; pub enum TypeSort { Struct, FnDef, + Closure, Trait, Opaque, } diff --git a/chalk-integration/src/lowering.rs b/chalk-integration/src/lowering.rs index 7a88e2388be..b0762087e18 100644 --- a/chalk-integration/src/lowering.rs +++ b/chalk-integration/src/lowering.rs @@ -26,6 +26,7 @@ type OpaqueTyIds = BTreeMap>; type AdtKinds = BTreeMap, TypeKind>; type FnDefKinds = BTreeMap, TypeKind>; type FnDefAbis = BTreeMap, ::FnAbi>; +type ClosureKinds = BTreeMap, TypeKind>; type TraitKinds = BTreeMap, TypeKind>; type OpaqueTyKinds = BTreeMap, TypeKind>; type AssociatedTyLookups = BTreeMap<(chalk_ir::TraitId, Ident), AssociatedTyLookup>; @@ -44,6 +45,7 @@ struct Env<'k> { fn_def_kinds: &'k FnDefKinds, fn_def_abis: &'k FnDefAbis, closure_ids: &'k ClosureIds, + closure_kinds: &'k ClosureKinds, trait_ids: &'k TraitIds, trait_kinds: &'k TraitKinds, opaque_ty_ids: &'k OpaqueTyIds, @@ -82,6 +84,7 @@ struct AssociatedTyLookup { enum ApplyTypeLookup { Adt(AdtId), FnDef(FnDefId), + Closure(ClosureId), Opaque(OpaqueTyId), } @@ -146,18 +149,8 @@ impl<'k> Env<'k> { if let Some(id) = self.closure_ids.get(&name.str) { return Ok(chalk_ir::TyData::Apply(chalk_ir::ApplicationTy { name: chalk_ir::TypeName::Closure(id.clone()), - // XXX closure upvars - substitution: chalk_ir::Substitution::from( - interner, - Some( - chalk_ir::TyData::Apply(chalk_ir::ApplicationTy { - name: chalk_ir::TypeName::Tuple(0), - substitution: chalk_ir::Substitution::empty(interner), - }) - .intern(interner) - .cast(interner), - ), - ), + // See note in `program`. Unlike rustc, we store upvars separately. + substitution: chalk_ir::Substitution::empty(interner), }) .intern(interner) .cast(interner)); @@ -193,6 +186,10 @@ impl<'k> Env<'k> { return Ok(ApplyTypeLookup::FnDef(*id)); } + if let Some(id) = self.closure_ids.get(&name.str) { + return Ok(ApplyTypeLookup::Closure(*id)); + } + if let Some(id) = self.opaque_ty_ids.get(&name.str) { return Ok(ApplyTypeLookup::Opaque(*id)); } @@ -228,6 +225,10 @@ impl<'k> Env<'k> { &self.fn_def_kinds[&id] } + fn closure_kind(&self, id: chalk_ir::ClosureId) -> &TypeKind { + &self.closure_kinds[&id] + } + fn opaque_kind(&self, id: chalk_ir::OpaqueTyId) -> &TypeKind { &self.opaque_ty_kinds[&id] } @@ -346,6 +347,7 @@ impl LowerProgram for Program { let mut adt_kinds = BTreeMap::new(); let mut fn_def_kinds = BTreeMap::new(); let mut fn_def_abis = BTreeMap::new(); + let mut closure_kinds = BTreeMap::new(); let mut trait_kinds = BTreeMap::new(); let mut opaque_ty_kinds = BTreeMap::new(); let mut object_safe_traits = HashSet::new(); @@ -365,8 +367,10 @@ impl LowerProgram for Program { fn_def_abis.insert(id, defn.abi.lower()?); } Item::ClosureDefn(defn) => { + let type_kind = defn.lower_type_kind()?; let id = ClosureId(raw_id); closure_ids.insert(defn.name.str.clone(), id); + closure_kinds.insert(id, type_kind); } Item::TraitDefn(defn) => { let type_kind = defn.lower_type_kind()?; @@ -392,6 +396,7 @@ impl LowerProgram for Program { let mut adt_data = BTreeMap::new(); let mut fn_def_data = BTreeMap::new(); let mut closure_data = BTreeMap::new(); + let mut closure_upvars = BTreeMap::new(); let mut trait_data = BTreeMap::new(); let mut well_known_traits = BTreeMap::new(); let mut impl_data = BTreeMap::new(); @@ -408,6 +413,7 @@ impl LowerProgram for Program { fn_def_kinds: &fn_def_kinds, fn_def_abis: &fn_def_abis, closure_ids: &closure_ids, + closure_kinds: &closure_kinds, trait_ids: &trait_ids, trait_kinds: &trait_kinds, opaque_ty_ids: &opaque_ty_ids, @@ -434,6 +440,20 @@ impl LowerProgram for Program { closure_def_id, Arc::new(defn.lower_closure(closure_def_id, &empty_env)?), ); + let upvars = empty_env.in_binders(defn.all_parameters(), |env| { + let upvar_tys: LowerResult>> = + defn.upvars.iter().map(|ty| ty.lower(&env)).collect(); + let substitution = chalk_ir::Substitution::from( + &ChalkIr, + upvar_tys?.into_iter().map(|ty| ty.cast(&ChalkIr)), + ); + Ok(chalk_ir::TyData::Apply(chalk_ir::ApplicationTy { + name: chalk_ir::TypeName::Tuple(defn.upvars.len()), + substitution, + }) + .intern(&ChalkIr)) + })?; + closure_upvars.insert(closure_def_id, upvars); } Item::TraitDefn(ref trait_defn) => { let trait_id = TraitId(raw_id); @@ -593,6 +613,8 @@ impl LowerProgram for Program { adt_ids, fn_def_ids, closure_ids, + closure_upvars, + closure_kinds, trait_ids, adt_kinds, fn_def_kinds, @@ -695,6 +717,16 @@ impl LowerParameterMap for FnDefn { } } +impl LowerParameterMap for ClosureDefn { + fn synthetic_parameters(&self) -> Option> { + None + } + + fn declared_parameters(&self) -> &[VariableKind] { + &self.variable_kinds + } +} + impl LowerParameterMap for Impl { fn synthetic_parameters(&self) -> Option> { None @@ -832,6 +864,20 @@ impl LowerWhereClauses for FnDefn { } } +impl LowerTypeKind for ClosureDefn { + fn lower_type_kind(&self) -> LowerResult { + let interner = &ChalkIr; + Ok(TypeKind { + sort: TypeSort::Closure, + name: self.name.str.clone(), + binders: chalk_ir::Binders::new( + chalk_ir::VariableKinds::from(interner, self.all_parameters().anonymize()), + crate::Unit, + ), + }) + } +} + impl LowerWhereClauses for StructDefn { fn where_clauses(&self) -> &[QuantifiedWhereClause] { &self.where_clauses @@ -1123,7 +1169,7 @@ impl LowerClosureDefn for ClosureDefn { closure_id: chalk_ir::ClosureId, env: &Env, ) -> LowerResult> { - let inputs_and_output = env.in_binders(vec![], |env| { + let inputs_and_output = env.in_binders(self.all_parameters(), |env| { let args: LowerResult<_> = self.argument_types.iter().map(|t| t.lower(env)).collect(); let return_type = self.return_type.lower(env)?; Ok(rust_ir::FnDefInputsAndOutputDatum { @@ -1453,6 +1499,9 @@ impl LowerTy for Ty { ApplyTypeLookup::FnDef(id) => { (chalk_ir::TypeName::FnDef(id), env.fn_def_kind(id)) } + ApplyTypeLookup::Closure(id) => { + (chalk_ir::TypeName::Closure(id), env.closure_kind(id)) + } ApplyTypeLookup::Opaque(id) => { (chalk_ir::TypeName::OpaqueType(id), env.opaque_kind(id)) } @@ -1852,6 +1901,7 @@ impl LowerGoal for Goal { adt_kinds: &program.adt_kinds, fn_def_kinds: &program.fn_def_kinds, fn_def_abis: &fn_def_abis, + closure_kinds: &program.closure_kinds, trait_kinds: &program.trait_kinds, opaque_ty_kinds: &program.opaque_ty_kinds, associated_ty_lookups: &associated_ty_lookups, diff --git a/chalk-integration/src/program.rs b/chalk-integration/src/program.rs index 155b96bfa9a..394d128ab64 100644 --- a/chalk-integration/src/program.rs +++ b/chalk-integration/src/program.rs @@ -3,8 +3,8 @@ use crate::{tls, Identifier, TypeKind}; use chalk_ir::could_match::CouldMatch; use chalk_ir::debug::Angle; use chalk_ir::{ - debug::SeparatorTraitRef, AdtId, AliasTy, ApplicationTy, AssocTypeId, ClosureId, FnDefId, - GenericArg, Goal, Goals, ImplId, Lifetime, OpaqueTy, OpaqueTyId, ProgramClause, + debug::SeparatorTraitRef, AdtId, AliasTy, ApplicationTy, AssocTypeId, Binders, ClosureId, + FnDefId, GenericArg, Goal, Goals, ImplId, Lifetime, OpaqueTy, OpaqueTyId, ProgramClause, ProgramClauseImplication, ProgramClauses, ProjectionTy, Substitution, TraitId, Ty, }; use chalk_solve::rust_ir::{ @@ -31,6 +31,10 @@ pub struct Program { pub closure_ids: BTreeMap>, + pub closure_upvars: BTreeMap, Binders>>, + + pub closure_kinds: BTreeMap, TypeKind>, + /// From trait name to item-id. Used during lowering only. pub trait_ids: BTreeMap>, @@ -420,7 +424,7 @@ impl RustIrDatabase for Program { fn closure_datum( &self, closure_id: ClosureId, - substs: &Substitution, + _substs: &Substitution, ) -> Arc> { self.closure_data[&closure_id].clone() } @@ -428,13 +432,12 @@ impl RustIrDatabase for Program { fn closure_upvars( &self, closure_id: ClosureId, - substs: &Substitution, - ) -> Ty { - substs - .parameters(self.interner()) - .last() - .unwrap() - .assert_ty_ref(self.interner()) - .clone() + _substs: &Substitution, + ) -> Binders> { + // This is different to how rustc does it. In rustc, + // upvars are stored in the last substitution parameter. + // It's just easier this way, and from `chalk-solve`s POV, + // this is opaque anyways. + self.closure_upvars[&closure_id].clone() } } diff --git a/chalk-parse/src/ast.rs b/chalk-parse/src/ast.rs index 867816608df..96cdf04751d 100644 --- a/chalk-parse/src/ast.rs +++ b/chalk-parse/src/ast.rs @@ -59,8 +59,10 @@ pub struct FnDefn { pub struct ClosureDefn { pub name: Identifier, pub kind: ClosureKind, + pub variable_kinds: Vec, pub argument_types: Vec, pub return_type: Ty, + pub upvars: Vec, } #[derive(Clone, Eq, PartialEq, Debug)] diff --git a/chalk-parse/src/parser.lalrpop b/chalk-parse/src/parser.lalrpop index c776d1ff952..97787a42033 100644 --- a/chalk-parse/src/parser.lalrpop +++ b/chalk-parse/src/parser.lalrpop @@ -104,18 +104,25 @@ FnArgs: Vec = { }; ClosureDefn: ClosureDefn = { - "closure" "(" ")" ";" => ClosureDefn { + "closure" > "(" ")" + "{" > "}" => ClosureDefn { name: n, kind: s, - argument_types: vec![], - return_type: Ty::Tuple { types: Vec::new() }, + variable_kinds: p, + argument_types: args, + return_type: ret_ty.unwrap_or_else(|| Ty::Tuple { types: Vec::new() }), + upvars: upvars, } } ClosureSelf: ClosureKind = { "self" => ClosureKind::FnOnce, - "mutself" => ClosureKind::FnMut, - "refself" => ClosureKind::Fn, + "&" "mut" "self" => ClosureKind::FnMut, + "&" "self" => ClosureKind::Fn, +} + +ClosureArgs: Vec = { + "," => args, } TraitDefn: TraitDefn = { diff --git a/chalk-solve/src/clauses/builtin_traits/copy.rs b/chalk-solve/src/clauses/builtin_traits/copy.rs index 5bb2fa9023f..4eb421f7818 100644 --- a/chalk-solve/src/clauses/builtin_traits/copy.rs +++ b/chalk-solve/src/clauses/builtin_traits/copy.rs @@ -56,7 +56,9 @@ pub fn add_copy_program_clauses( } TypeName::Closure(closure_id) => { let upvars = db.closure_upvars(*closure_id, substitution); - needs_impl_for_tys(db, builder, trait_ref, Some(upvars).into_iter()); + builder.push_binders(&upvars, |builder, upvars| { + needs_impl_for_tys(db, builder, trait_ref, Some(upvars).into_iter()); + }) } _ => return, }, diff --git a/chalk-solve/src/clauses/builtin_traits/fn_family.rs b/chalk-solve/src/clauses/builtin_traits/fn_family.rs index a502c62097e..dd79c44b197 100644 --- a/chalk-solve/src/clauses/builtin_traits/fn_family.rs +++ b/chalk-solve/src/clauses/builtin_traits/fn_family.rs @@ -4,10 +4,55 @@ use crate::rust_ir::{ClosureKind, WellKnownTrait}; use crate::{Interner, RustIrDatabase, TraitRef}; use chalk_ir::cast::Cast; use chalk_ir::{ - AliasTy, ApplicationTy, Binders, Floundered, Normalize, ProjectionTy, Substitution, Ty, TyData, - TypeName, VariableKinds, + AliasTy, ApplicationTy, Binders, Floundered, Normalize, ProjectionTy, Substitution, TraitId, + Ty, TyData, TypeName, VariableKinds, }; +fn push_clauses( + db: &dyn RustIrDatabase, + builder: &mut ClauseBuilder<'_, I>, + well_known: WellKnownTrait, + trait_id: TraitId, + self_ty: Ty, + arg_sub: Substitution, + return_type: Ty, +) { + let interner = db.interner(); + let tupled = TyData::Apply(ApplicationTy { + name: TypeName::Tuple(arg_sub.len(interner)), + substitution: arg_sub, + }) + .intern(interner); + let substitution = + Substitution::from(interner, &[self_ty.cast(interner), tupled.cast(interner)]); + builder.push_fact(TraitRef { + trait_id, + substitution: substitution.clone(), + }); + + // The `Output` type is defined on the `FnOnce` + if let WellKnownTrait::FnOnce = well_known { + let trait_datum = db.trait_datum(trait_id); + assert_eq!( + trait_datum.associated_ty_ids.len(), + 1, + "FnOnce trait should have exactly one associated type, found {:?}", + trait_datum.associated_ty_ids + ); + // Constructs the alias. For `Fn`, for example, this would look like + // `Normalize( B as FnOnce<(A,)>>::Output -> B)` + let output_id = trait_datum.associated_ty_ids[0]; + let alias = AliasTy::Projection(ProjectionTy { + associated_ty_id: output_id, + substitution, + }); + builder.push_fact(Normalize { + alias, + ty: return_type, + }); + } +} + /// Handles clauses for FnOnce/FnMut/Fn. /// If `self_ty` is a function, we push a clause of the form /// `fn(A1, A2, ..., AN) -> O: FnTrait<(A1, A2, ..., AN)>`, where `FnTrait` @@ -26,44 +71,6 @@ pub fn add_fn_trait_program_clauses( let interner = db.interner(); let trait_id = db.well_known_trait_id(well_known).unwrap(); - let push_clauses = |builder: &mut ClauseBuilder<'_, I>, - self_ty: Ty, - arg_sub: Substitution, - return_type| { - let tupled = TyData::Apply(ApplicationTy { - name: TypeName::Tuple(arg_sub.len(interner)), - substitution: arg_sub, - }) - .intern(interner); - let substitution = - Substitution::from(interner, &[self_ty.cast(interner), tupled.cast(interner)]); - builder.push_fact(TraitRef { - trait_id, - substitution: substitution.clone(), - }); - - // The `Output` type is defined on the `FnOnce` - if let WellKnownTrait::FnOnce = well_known { - let trait_datum = db.trait_datum(trait_id); - assert_eq!( - trait_datum.associated_ty_ids.len(), - 1, - "FnOnce trait should have exactly one associated type, found {:?}", - trait_datum.associated_ty_ids - ); - // Constructs the alias. For `Fn`, for example, this would look like - // `Normalize( B as FnOnce<(A,)>>::Output -> B)` - let output_id = trait_datum.associated_ty_ids[0]; - let alias = AliasTy::Projection(ProjectionTy { - associated_ty_id: output_id, - substitution, - }); - builder.push_fact(Normalize { - alias, - ty: return_type, - }); - } - }; match self_ty.data(interner) { TyData::Apply(apply) => match apply.name { TypeName::FnDef(fn_def_id) => { @@ -87,7 +94,9 @@ pub fn add_fn_trait_program_clauses( let arg_sub = Substitution::from(interner, arg_sub); let output_ty = inputs_and_output.return_type; - push_clauses(builder, self_ty, arg_sub, output_ty); + push_clauses( + db, builder, well_known, trait_id, self_ty, arg_sub, output_ty, + ); }); Ok(()) } @@ -113,7 +122,9 @@ pub fn add_fn_trait_program_clauses( let arg_sub = Substitution::from(interner, arg_sub); let output_ty = inputs_and_output.return_type; - push_clauses(builder, self_ty, arg_sub, output_ty); + push_clauses( + db, builder, well_known, trait_id, self_ty, arg_sub, output_ty, + ); }); Ok(()) } @@ -130,7 +141,15 @@ pub fn add_fn_trait_program_clauses( let arg_sub = Substitution::from(interner, arg_sub); let output_ty = fn_output_ty[0].assert_ty_ref(interner).clone(); - push_clauses(builder, self_ty.clone(), arg_sub, output_ty); + push_clauses( + db, + builder, + well_known, + trait_id, + self_ty.clone(), + arg_sub, + output_ty, + ); }); Ok(()) } diff --git a/chalk-solve/src/lib.rs b/chalk-solve/src/lib.rs index 83bc606c91b..2e75044aed3 100644 --- a/chalk-solve/src/lib.rs +++ b/chalk-solve/src/lib.rs @@ -108,7 +108,7 @@ pub trait RustIrDatabase: Debug { substs: &Substitution, ) -> Arc>; - fn closure_upvars(&self, closure_id: ClosureId, substs: &Substitution) -> Ty; + fn closure_upvars(&self, closure_id: ClosureId, substs: &Substitution) -> Binders>; } pub use clauses::program_clauses_for_env; diff --git a/tests/integration/panic.rs b/tests/integration/panic.rs index 7173e37fa0f..e49df922849 100644 --- a/tests/integration/panic.rs +++ b/tests/integration/panic.rs @@ -198,7 +198,7 @@ impl RustIrDatabase for MockDatabase { &self, closure_id: ClosureId, substs: &Substitution, - ) -> Ty { + ) -> Binders> { unimplemented!() } } diff --git a/tests/lowering/mod.rs b/tests/lowering/mod.rs index 37ac8ab15e1..66eecd85d21 100644 --- a/tests/lowering/mod.rs +++ b/tests/lowering/mod.rs @@ -722,9 +722,18 @@ fn extern_functions() { fn closures() { lowering_success! { program { - closure foo(self); - closure bar(refself); - closure baz(mutself); + closure foo(self,) {} + closure bar(&self,) {} + closure baz(&mut self,) {} + + closure buzz(self,) -> u32 {} + closure foobar<'a>(self,) -> u32 {} + closure foobaz<'a>(self, a: u8, b: f32) -> u32 {} + closure foobuzz<'a>(self, a: u8, b: f32) -> u32 { + u8; + &'a u16; + &'a mut u32 + } } } } diff --git a/tests/test/closures.rs b/tests/test/closures.rs index 7e7ba4a1585..8361832bbb4 100644 --- a/tests/test/closures.rs +++ b/tests/test/closures.rs @@ -4,9 +4,9 @@ use super::*; fn closure_is_well_formed() { test! { program { - closure foo(self); - closure bar(refself); - closure baz(mutself); + closure foo(self,) {} + closure bar(&self,) {} + closure baz(&mut self,) {} } goal { @@ -34,7 +34,7 @@ fn closure_is_sized() { #[lang(sized)] trait Sized { } - closure foo(self); + closure foo(self,) {} } goal { @@ -51,12 +51,27 @@ fn closure_is_copy() { program { #[lang(copy)] trait Copy { } + impl<'a, T> Copy for &'a T {} + impl Copy for u8 {} + impl Copy for u16 {} + impl Copy for u32 {} - closure foo(self); - closure bar(refself); - closure baz(mutself); + closure foo(self,) {} + closure bar(&self,) {} + closure baz(&mut self,) {} + + closure foobuzz<'a>(self, a: u8, b: f32) -> u32 { + u8; + &'a u16; + &'a mut u32 + } + closure foobar<'a>(self, a: u8, b: f32) -> u32 { + u8; + &'a u16 + } } + // A closure with no upvars is also copy, regardless of kind goal { foo: Copy } yields { @@ -72,6 +87,19 @@ fn closure_is_copy() { } yields { "Unique" } + + // A closure with non-Copy upvars is not copy + goal { + foobuzz: Copy + } yields { + "No possible solution" + } + // A closure with only Copy upvars is copy + goal { + foobar: Copy + } yields { + "Unique" + } } } @@ -82,9 +110,9 @@ fn closure_is_clone() { #[lang(clone)] trait Clone { } - closure foo(self); - closure bar(refself); - closure baz(mutself); + closure foo(self,) {} + closure bar(&self,) {} + closure baz(&mut self,) {} } goal { foo: Clone @@ -119,11 +147,22 @@ fn closure_implements_fn_traits() { #[lang(fn)] trait Fn where Self: FnMut { } - closure foo(self); - closure bar(refself); - closure baz(mutself); + closure foo(self,) {} + closure bar(&self,) {} + closure baz(&mut self,) {} + + closure foobuzz<'a>(self, a: u8, b: f32) -> u32 { + u8; + &'a u16; + &'a mut u32 + } + closure foobar<'a>(self, a: u8, b: f32) -> u32 { + u8; + &'a u16 + } } + // A closure with kind `FnOnce` only implements `FnOnce` goal { foo: Fn<()> } yields { @@ -145,6 +184,7 @@ fn closure_implements_fn_traits() { "Unique" } + // A closure with kind `Fn` implements all `Fn` traits goal { bar: Fn<()> } yields { @@ -166,6 +206,7 @@ fn closure_implements_fn_traits() { "Unique" } + // A closure with kind `FnMut` implements `FnMut` and `FnOnce` goal { baz: Fn<()> } yields { @@ -186,5 +227,33 @@ fn closure_implements_fn_traits() { } yields { "Unique" } + // A closure also implements the `Fn` traits regardless of upvars + goal { + foobar: FnOnce<(u8, f32)> + } yields { + "Unique" + } + goal { + Normalize(>::Output -> u32) + } yields { + "Unique" + } + goal { + forall<'a> { + Normalize( as FnOnce<(u8, f32)>>::Output -> u32) + } + } yields { + "Unique" + } + goal { + foobuzz: FnOnce<(u8, f32)> + } yields { + "Unique" + } + goal { + Normalize(>::Output -> u32) + } yields { + "Unique" + } } } From 23fd109c79a9ec2e22cc3d0111bc10c4fed8673b Mon Sep 17 00:00:00 2001 From: Jack Huey Date: Tue, 16 Jun 2020 00:55:14 -0400 Subject: [PATCH 5/7] Fmt --- chalk-ir/src/visit/boring_impls.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chalk-ir/src/visit/boring_impls.rs b/chalk-ir/src/visit/boring_impls.rs index 511417b00b0..34f10c79afe 100644 --- a/chalk-ir/src/visit/boring_impls.rs +++ b/chalk-ir/src/visit/boring_impls.rs @@ -7,8 +7,8 @@ use crate::{ AdtId, AssocTypeId, ClausePriority, ClosureId, DebruijnIndex, FloatTy, FnDefId, GenericArg, Goals, ImplId, IntTy, Interner, Mutability, OpaqueTyId, PlaceholderIndex, ProgramClause, - ProgramClauses, QuantifiedWhereClauses, QuantifierKind, Scalar, - Substitution, SuperVisit, TraitId, UintTy, UniverseIndex, Visit, VisitResult, Visitor, + ProgramClauses, QuantifiedWhereClauses, QuantifierKind, Scalar, Substitution, SuperVisit, + TraitId, UintTy, UniverseIndex, Visit, VisitResult, Visitor, }; use std::{marker::PhantomData, sync::Arc}; From d02a88504b8ae94d853f45885412844e5e5b176c Mon Sep 17 00:00:00 2001 From: Jack Huey Date: Tue, 16 Jun 2020 13:19:15 -0400 Subject: [PATCH 6/7] Review comments. Test is broken --- chalk-integration/src/lowering.rs | 23 ++++-- .../src/clauses/builtin_traits/fn_family.rs | 81 +++++++++++-------- chalk-solve/src/lib.rs | 6 ++ chalk-solve/src/rust_ir.rs | 19 +++++ tests/test/closures.rs | 35 ++++++-- 5 files changed, 116 insertions(+), 48 deletions(-) diff --git a/chalk-integration/src/lowering.rs b/chalk-integration/src/lowering.rs index b0762087e18..9356d25ce3a 100644 --- a/chalk-integration/src/lowering.rs +++ b/chalk-integration/src/lowering.rs @@ -147,13 +147,22 @@ impl<'k> Env<'k> { } if let Some(id) = self.closure_ids.get(&name.str) { - return Ok(chalk_ir::TyData::Apply(chalk_ir::ApplicationTy { - name: chalk_ir::TypeName::Closure(id.clone()), - // See note in `program`. Unlike rustc, we store upvars separately. - substitution: chalk_ir::Substitution::empty(interner), - }) - .intern(interner) - .cast(interner)); + let k = self.closure_kind(*id); + if k.binders.len(interner) > 0 { + return Err(RustIrError::IncorrectNumberOfTypeParameters { + identifier: name.clone(), + expected: k.binders.len(interner), + actual: 0, + }); + } else { + return Ok(chalk_ir::TyData::Apply(chalk_ir::ApplicationTy { + name: chalk_ir::TypeName::Closure(id.clone()), + // See note in `program`. Unlike rustc, we store upvars separately. + substitution: chalk_ir::Substitution::empty(interner), + }) + .intern(interner) + .cast(interner)); + } } if let Some(id) = self.opaque_ty_ids.get(&name.str) { diff --git a/chalk-solve/src/clauses/builtin_traits/fn_family.rs b/chalk-solve/src/clauses/builtin_traits/fn_family.rs index dd79c44b197..1844d421933 100644 --- a/chalk-solve/src/clauses/builtin_traits/fn_family.rs +++ b/chalk-solve/src/clauses/builtin_traits/fn_family.rs @@ -1,6 +1,6 @@ use crate::clauses::ClauseBuilder; use crate::infer::instantiate::IntoBindersAndValue; -use crate::rust_ir::{ClosureKind, WellKnownTrait}; +use crate::rust_ir::{ClosureKind, FnDefInputsAndOutputDatum, WellKnownTrait}; use crate::{Interner, RustIrDatabase, TraitRef}; use chalk_ir::cast::Cast; use chalk_ir::{ @@ -53,6 +53,30 @@ fn push_clauses( } } +fn push_clauses_for_apply( + db: &dyn RustIrDatabase, + builder: &mut ClauseBuilder<'_, I>, + well_known: WellKnownTrait, + trait_id: TraitId, + self_ty: Ty, + inputs_and_output: &Binders>, +) { + let interner = db.interner(); + builder.push_binders(inputs_and_output, |builder, inputs_and_output| { + let arg_sub = inputs_and_output + .argument_types + .iter() + .cloned() + .map(|ty| ty.cast(interner)); + let arg_sub = Substitution::from(interner, arg_sub); + let output_ty = inputs_and_output.return_type; + + push_clauses( + db, builder, well_known, trait_id, self_ty, arg_sub, output_ty, + ); + }); +} + /// Handles clauses for FnOnce/FnMut/Fn. /// If `self_ty` is a function, we push a clause of the form /// `fn(A1, A2, ..., AN) -> O: FnTrait<(A1, A2, ..., AN)>`, where `FnTrait` @@ -78,26 +102,19 @@ pub fn add_fn_trait_program_clauses( let bound = fn_def_datum .binders .substitute(builder.interner(), &apply.substitution); - let inputs_and_output = &bound.inputs_and_output; - builder.push_binders(inputs_and_output, |builder, inputs_and_output| { - let self_ty = ApplicationTy { - name: apply.name, - substitution: builder.substitution_in_scope(), - } - .intern(interner); - - let arg_sub = inputs_and_output - .argument_types - .iter() - .cloned() - .map(|ty| ty.cast(interner)); - let arg_sub = Substitution::from(interner, arg_sub); - let output_ty = inputs_and_output.return_type; - - push_clauses( - db, builder, well_known, trait_id, self_ty, arg_sub, output_ty, - ); - }); + let self_ty = ApplicationTy { + name: apply.name, + substitution: builder.substitution_in_scope(), + } + .intern(interner); + push_clauses_for_apply( + db, + builder, + well_known, + trait_id, + self_ty, + &bound.inputs_and_output, + ); Ok(()) } TypeName::Closure(closure_id) => { @@ -112,20 +129,14 @@ pub fn add_fn_trait_program_clauses( if !trait_matches { return Ok(()); } - let inputs_and_output = &closure_datum.inputs_and_output; - builder.push_binders(inputs_and_output, |builder, inputs_and_output| { - let arg_sub = inputs_and_output - .argument_types - .iter() - .cloned() - .map(|ty| ty.cast(interner)); - let arg_sub = Substitution::from(interner, arg_sub); - let output_ty = inputs_and_output.return_type; - - push_clauses( - db, builder, well_known, trait_id, self_ty, arg_sub, output_ty, - ); - }); + push_clauses_for_apply( + db, + builder, + well_known, + trait_id, + self_ty, + &closure_datum.inputs_and_output, + ); Ok(()) } _ => Ok(()), diff --git a/chalk-solve/src/lib.rs b/chalk-solve/src/lib.rs index 2e75044aed3..388212ada2d 100644 --- a/chalk-solve/src/lib.rs +++ b/chalk-solve/src/lib.rs @@ -102,12 +102,18 @@ pub trait RustIrDatabase: Debug { /// Check if a trait is object safe fn is_object_safe(&self, trait_id: TraitId) -> bool; + /// Gets the `ClosureDatum` for a given closure id and substitution. We + /// pass both the `ClosureId` and it's `Substituion` to give implementors + /// the freedom to store associated data in the substitution (like rustc) or + /// separately (like chalk-integration). fn closure_datum( &self, closure_id: ClosureId, substs: &Substitution, ) -> Arc>; + /// Gets the upvars as a `Ty` for a given closure id and substitution. There + /// are no restrictions on the type of upvars. fn closure_upvars(&self, closure_id: ClosureId, substs: &Substitution) -> Binders>; } diff --git a/chalk-solve/src/rust_ir.rs b/chalk-solve/src/rust_ir.rs index c1ee0b7dc63..8613606b183 100644 --- a/chalk-solve/src/rust_ir.rs +++ b/chalk-solve/src/rust_ir.rs @@ -168,12 +168,28 @@ pub struct FnDefDatumBound { pub where_clauses: Vec>, } +/// Represents a closure. +/// For example, for the following rust code: +/// ```ignore +/// let mut a = 0; +/// let foo = |b: &mut u32| -> u32 { a += 1; b += 1; 0 }; +/// let mut b = 0; +/// foo(&mut b); +/// ``` +/// +/// This declares a closure `foo` with one bound lifetime, an input of +/// `&mut u32`, an output of `u32`, and a kind of `ClosureKind::FnMut`. #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct ClosureDatum { + /// The id of the closure. pub id: ClosureId, + /// The inputs and outputs of closure, in `Binders`. See `FnDef` for a bit + /// more detailed explanation. pub inputs_and_output: Binders>, + /// The kind of the closure. This is calculated based on the upvars that + /// are captured. pub kind: ClosureKind, } @@ -601,6 +617,9 @@ impl Polarity { } } +/// Indicates the "most permissive" Fn-like trait that the closure implements. +/// If the closure kind for a closure is FnMut, for example, then the closure +/// implements FnMut and FnOnce. #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] pub enum ClosureKind { Fn, diff --git a/tests/test/closures.rs b/tests/test/closures.rs index 8361832bbb4..37fe647fb07 100644 --- a/tests/test/closures.rs +++ b/tests/test/closures.rs @@ -69,6 +69,7 @@ fn closure_is_copy() { u8; &'a u16 } + closure with_ty(self,) { T } } // A closure with no upvars is also copy, regardless of kind @@ -90,13 +91,27 @@ fn closure_is_copy() { // A closure with non-Copy upvars is not copy goal { - foobuzz: Copy + forall<'a> { + foobuzz<'a>: Copy + } } yields { "No possible solution" } // A closure with only Copy upvars is copy goal { - foobar: Copy + forall<'a> { + foobar<'a>: Copy + } + } yields { + "Unique" + } + goal { + forall { with_ty: Copy } + } yields { + "No possible solution" + } + goal { + forall { if (T: Copy) { with_ty: Copy } } } yields { "Unique" } @@ -229,12 +244,16 @@ fn closure_implements_fn_traits() { } // A closure also implements the `Fn` traits regardless of upvars goal { - foobar: FnOnce<(u8, f32)> + forall<'a> { + foobar<'a>: FnOnce<(u8, f32)> + } } yields { "Unique" } goal { - Normalize(>::Output -> u32) + forall<'a> { + Normalize( as FnOnce<(u8, f32)>>::Output -> u32) + } } yields { "Unique" } @@ -246,12 +265,16 @@ fn closure_implements_fn_traits() { "Unique" } goal { - foobuzz: FnOnce<(u8, f32)> + forall<'a> { + foobuzz<'a>: FnOnce<(u8, f32)> + } } yields { "Unique" } goal { - Normalize(>::Output -> u32) + forall<'a> { + Normalize( as FnOnce<(u8, f32)>>::Output -> u32) + } } yields { "Unique" } From 08efff09736876f9571aa2d8b807e9ecfe464f1c Mon Sep 17 00:00:00 2001 From: Jack Huey Date: Tue, 16 Jun 2020 14:33:37 -0400 Subject: [PATCH 7/7] Remove ClosureDatum and replace with functions on RustIrDatabase directly --- chalk-integration/src/db.rs | 30 +++++++++++--- chalk-integration/src/lowering.rs | 31 +++++++------- chalk-integration/src/program.rs | 41 ++++++++++++++----- .../src/clauses/builtin_traits/copy.rs | 8 ++-- .../src/clauses/builtin_traits/fn_family.rs | 8 ++-- chalk-solve/src/lib.rs | 25 +++++++++-- chalk-solve/src/rust_ir.rs | 31 ++------------ tests/integration/panic.rs | 20 ++++++++- 8 files changed, 123 insertions(+), 71 deletions(-) diff --git a/chalk-integration/src/db.rs b/chalk-integration/src/db.rs index f79d5bf791c..b67081a60ac 100644 --- a/chalk-integration/src/db.rs +++ b/chalk-integration/src/db.rs @@ -12,8 +12,8 @@ use chalk_ir::{ Substitution, TraitId, Ty, UCanonical, }; use chalk_solve::rust_ir::{ - AdtDatum, AssociatedTyDatum, AssociatedTyValue, AssociatedTyValueId, ClosureDatum, FnDefDatum, - ImplDatum, OpaqueTyDatum, TraitDatum, WellKnownTrait, + AdtDatum, AssociatedTyDatum, AssociatedTyValue, AssociatedTyValueId, ClosureKind, FnDefDatum, + FnDefInputsAndOutputDatum, ImplDatum, OpaqueTyDatum, TraitDatum, WellKnownTrait, }; use chalk_solve::{RustIrDatabase, Solution, SolverChoice, SubstitutionResult}; use salsa::Database; @@ -156,12 +156,22 @@ impl RustIrDatabase for ChalkDatabase { self.program_ir().unwrap().is_object_safe(trait_id) } - fn closure_datum( + fn closure_inputs_and_output( &self, closure_id: ClosureId, substs: &Substitution, - ) -> Arc> { - self.program_ir().unwrap().closure_datum(closure_id, substs) + ) -> Binders> { + self.program_ir() + .unwrap() + .closure_inputs_and_output(closure_id, substs) + } + + fn closure_kind( + &self, + closure_id: ClosureId, + substs: &Substitution, + ) -> ClosureKind { + self.program_ir().unwrap().closure_kind(closure_id, substs) } fn closure_upvars( @@ -173,4 +183,14 @@ impl RustIrDatabase for ChalkDatabase { .unwrap() .closure_upvars(closure_id, substs) } + + fn closure_fn_substitution( + &self, + closure_id: ClosureId, + substs: &Substitution, + ) -> Substitution { + self.program_ir() + .unwrap() + .closure_fn_substitution(closure_id, substs) + } } diff --git a/chalk-integration/src/lowering.rs b/chalk-integration/src/lowering.rs index 9356d25ce3a..fdb9ee768d3 100644 --- a/chalk-integration/src/lowering.rs +++ b/chalk-integration/src/lowering.rs @@ -404,7 +404,8 @@ impl LowerProgram for Program { let mut adt_data = BTreeMap::new(); let mut fn_def_data = BTreeMap::new(); - let mut closure_data = BTreeMap::new(); + let mut closure_inputs_and_output = BTreeMap::new(); + let mut closure_closure_kind = BTreeMap::new(); let mut closure_upvars = BTreeMap::new(); let mut trait_data = BTreeMap::new(); let mut well_known_traits = BTreeMap::new(); @@ -445,10 +446,9 @@ impl LowerProgram for Program { } Item::ClosureDefn(ref defn) => { let closure_def_id = ClosureId(raw_id); - closure_data.insert( - closure_def_id, - Arc::new(defn.lower_closure(closure_def_id, &empty_env)?), - ); + let (kind, inputs_and_output) = defn.lower_closure(&empty_env)?; + closure_closure_kind.insert(closure_def_id, kind); + closure_inputs_and_output.insert(closure_def_id, inputs_and_output); let upvars = empty_env.in_binders(defn.all_parameters(), |env| { let upvar_tys: LowerResult>> = defn.upvars.iter().map(|ty| ty.lower(&env)).collect(); @@ -630,7 +630,8 @@ impl LowerProgram for Program { trait_kinds, adt_data, fn_def_data, - closure_data, + closure_inputs_and_output, + closure_closure_kind, trait_data, well_known_traits, impl_data, @@ -1167,17 +1168,21 @@ impl LowerFnAbi for FnAbi { trait LowerClosureDefn { fn lower_closure( &self, - closure_id: chalk_ir::ClosureId, env: &Env, - ) -> LowerResult>; + ) -> LowerResult<( + rust_ir::ClosureKind, + chalk_ir::Binders>, + )>; } impl LowerClosureDefn for ClosureDefn { fn lower_closure( &self, - closure_id: chalk_ir::ClosureId, env: &Env, - ) -> LowerResult> { + ) -> LowerResult<( + rust_ir::ClosureKind, + chalk_ir::Binders>, + )> { let inputs_and_output = env.in_binders(self.all_parameters(), |env| { let args: LowerResult<_> = self.argument_types.iter().map(|t| t.lower(env)).collect(); let return_type = self.return_type.lower(env)?; @@ -1187,11 +1192,7 @@ impl LowerClosureDefn for ClosureDefn { }) })?; - Ok(rust_ir::ClosureDatum { - id: closure_id, - kind: self.kind.lower_closure_kind()?, - inputs_and_output, - }) + Ok((self.kind.lower_closure_kind()?, inputs_and_output)) } } diff --git a/chalk-integration/src/program.rs b/chalk-integration/src/program.rs index 394d128ab64..36360be92b6 100644 --- a/chalk-integration/src/program.rs +++ b/chalk-integration/src/program.rs @@ -8,8 +8,8 @@ use chalk_ir::{ ProgramClauseImplication, ProgramClauses, ProjectionTy, Substitution, TraitId, Ty, }; use chalk_solve::rust_ir::{ - AdtDatum, AssociatedTyDatum, AssociatedTyValue, AssociatedTyValueId, ClosureDatum, FnDefDatum, - ImplDatum, ImplType, OpaqueTyDatum, TraitDatum, WellKnownTrait, + AdtDatum, AssociatedTyDatum, AssociatedTyValue, AssociatedTyValueId, ClosureKind, FnDefDatum, + FnDefInputsAndOutputDatum, ImplDatum, ImplType, OpaqueTyDatum, TraitDatum, WellKnownTrait, }; use chalk_solve::split::Split; use chalk_solve::RustIrDatabase; @@ -46,7 +46,11 @@ pub struct Program { pub fn_def_data: BTreeMap, Arc>>, - pub closure_data: BTreeMap, Arc>>, + pub closure_inputs_and_output: + BTreeMap, Binders>>, + + // Weird name, but otherwise would overlap with `closure_kinds` above. + pub closure_closure_kind: BTreeMap, ClosureKind>, /// For each impl: pub impl_data: BTreeMap, Arc>>, @@ -421,12 +425,25 @@ impl RustIrDatabase for Program { self.object_safe_traits.contains(&trait_id) } - fn closure_datum( + // For all the closure functions: this is different than how rustc does it. + // In rustc, the substitution, closure kind, fnsig, and upvars are stored + // together. Here, we store the closure kind, signature, and upvars + // separately, since it's easier. And this is opaque to `chalk-solve`. + + fn closure_inputs_and_output( + &self, + closure_id: ClosureId, + _substs: &Substitution, + ) -> Binders> { + self.closure_inputs_and_output[&closure_id].clone() + } + + fn closure_kind( &self, closure_id: ClosureId, _substs: &Substitution, - ) -> Arc> { - self.closure_data[&closure_id].clone() + ) -> ClosureKind { + self.closure_closure_kind[&closure_id] } fn closure_upvars( @@ -434,10 +451,14 @@ impl RustIrDatabase for Program { closure_id: ClosureId, _substs: &Substitution, ) -> Binders> { - // This is different to how rustc does it. In rustc, - // upvars are stored in the last substitution parameter. - // It's just easier this way, and from `chalk-solve`s POV, - // this is opaque anyways. self.closure_upvars[&closure_id].clone() } + + fn closure_fn_substitution( + &self, + _closure_id: ClosureId, + substs: &Substitution, + ) -> Substitution { + substs.clone() + } } diff --git a/chalk-solve/src/clauses/builtin_traits/copy.rs b/chalk-solve/src/clauses/builtin_traits/copy.rs index 4eb421f7818..7642ef15781 100644 --- a/chalk-solve/src/clauses/builtin_traits/copy.rs +++ b/chalk-solve/src/clauses/builtin_traits/copy.rs @@ -35,8 +35,6 @@ pub fn add_copy_program_clauses( trait_ref: &TraitRef, ty: &TyData, ) { - let _interner = db.interner(); - match ty { TyData::Apply(ApplicationTy { name, substitution }) => match name { TypeName::Tuple(arity) => { @@ -55,10 +53,10 @@ pub fn add_copy_program_clauses( builder.push_fact(trait_ref.clone()); } TypeName::Closure(closure_id) => { + let closure_fn_substitution = db.closure_fn_substitution(*closure_id, substitution); let upvars = db.closure_upvars(*closure_id, substitution); - builder.push_binders(&upvars, |builder, upvars| { - needs_impl_for_tys(db, builder, trait_ref, Some(upvars).into_iter()); - }) + let upvars = upvars.substitute(db.interner(), &closure_fn_substitution); + needs_impl_for_tys(db, builder, trait_ref, Some(upvars).into_iter()); } _ => return, }, diff --git a/chalk-solve/src/clauses/builtin_traits/fn_family.rs b/chalk-solve/src/clauses/builtin_traits/fn_family.rs index 1844d421933..ca878026b98 100644 --- a/chalk-solve/src/clauses/builtin_traits/fn_family.rs +++ b/chalk-solve/src/clauses/builtin_traits/fn_family.rs @@ -118,8 +118,8 @@ pub fn add_fn_trait_program_clauses( Ok(()) } TypeName::Closure(closure_id) => { - let closure_datum = db.closure_datum(closure_id, &apply.substitution); - let trait_matches = match (well_known, closure_datum.kind) { + let closure_kind = db.closure_kind(closure_id, &apply.substitution); + let trait_matches = match (well_known, closure_kind) { (WellKnownTrait::Fn, ClosureKind::Fn) => true, (WellKnownTrait::FnMut, ClosureKind::FnMut) | (WellKnownTrait::FnMut, ClosureKind::Fn) => true, @@ -129,13 +129,15 @@ pub fn add_fn_trait_program_clauses( if !trait_matches { return Ok(()); } + let closure_inputs_and_output = + db.closure_inputs_and_output(closure_id, &apply.substitution); push_clauses_for_apply( db, builder, well_known, trait_id, self_ty, - &closure_datum.inputs_and_output, + &closure_inputs_and_output, ); Ok(()) } diff --git a/chalk-solve/src/lib.rs b/chalk-solve/src/lib.rs index 388212ada2d..ff47aaedba4 100644 --- a/chalk-solve/src/lib.rs +++ b/chalk-solve/src/lib.rs @@ -102,19 +102,38 @@ pub trait RustIrDatabase: Debug { /// Check if a trait is object safe fn is_object_safe(&self, trait_id: TraitId) -> bool; - /// Gets the `ClosureDatum` for a given closure id and substitution. We + /// Gets the `ClosureKind` for a given closure and substitution. + fn closure_kind(&self, closure_id: ClosureId, substs: &Substitution) -> ClosureKind; + + /// Gets the inputs and output for a given closure id and substitution. We /// pass both the `ClosureId` and it's `Substituion` to give implementors /// the freedom to store associated data in the substitution (like rustc) or /// separately (like chalk-integration). - fn closure_datum( + fn closure_inputs_and_output( &self, closure_id: ClosureId, substs: &Substitution, - ) -> Arc>; + ) -> Binders>; /// Gets the upvars as a `Ty` for a given closure id and substitution. There /// are no restrictions on the type of upvars. fn closure_upvars(&self, closure_id: ClosureId, substs: &Substitution) -> Binders>; + + /// Gets the substitution for the closure when used as a function. + /// For example, for the following (not-quite-)rust code: + /// ```ignore + /// let foo = |a: &mut u32| { a += 1; }; + /// let c: &'a u32 = &0; + /// foo(c); + /// ``` + /// + /// This would return a `Substitution` of `[&'a]`. This could either be + /// substituted into the inputs and output, or into the upvars. + fn closure_fn_substitution( + &self, + closure_id: ClosureId, + substs: &Substitution, + ) -> Substitution; } pub use clauses::program_clauses_for_env; diff --git a/chalk-solve/src/rust_ir.rs b/chalk-solve/src/rust_ir.rs index 8613606b183..7b576539dd4 100644 --- a/chalk-solve/src/rust_ir.rs +++ b/chalk-solve/src/rust_ir.rs @@ -9,9 +9,9 @@ use chalk_ir::cast::Cast; use chalk_ir::fold::shift::Shift; use chalk_ir::interner::{Interner, TargetInterner}; use chalk_ir::{ - AdtId, AliasEq, AliasTy, AssocTypeId, Binders, ClosureId, DebruijnIndex, FnDefId, GenericArg, - ImplId, OpaqueTyId, ProjectionTy, QuantifiedWhereClause, Substitution, ToGenericArg, TraitId, - TraitRef, Ty, TyData, TypeName, VariableKind, WhereClause, WithKind, + AdtId, AliasEq, AliasTy, AssocTypeId, Binders, DebruijnIndex, FnDefId, GenericArg, ImplId, + OpaqueTyId, ProjectionTy, QuantifiedWhereClause, Substitution, ToGenericArg, TraitId, TraitRef, + Ty, TyData, TypeName, VariableKind, WhereClause, WithKind, }; use std::iter; @@ -168,31 +168,6 @@ pub struct FnDefDatumBound { pub where_clauses: Vec>, } -/// Represents a closure. -/// For example, for the following rust code: -/// ```ignore -/// let mut a = 0; -/// let foo = |b: &mut u32| -> u32 { a += 1; b += 1; 0 }; -/// let mut b = 0; -/// foo(&mut b); -/// ``` -/// -/// This declares a closure `foo` with one bound lifetime, an input of -/// `&mut u32`, an output of `u32`, and a kind of `ClosureKind::FnMut`. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct ClosureDatum { - /// The id of the closure. - pub id: ClosureId, - - /// The inputs and outputs of closure, in `Binders`. See `FnDef` for a bit - /// more detailed explanation. - pub inputs_and_output: Binders>, - - /// The kind of the closure. This is calculated based on the upvars that - /// are captured. - pub kind: ClosureKind, -} - #[derive(Clone, Debug, PartialEq, Eq, Hash)] /// A rust intermediate representation (rust_ir) of a Trait Definition. For /// example, given the following rust code: diff --git a/tests/integration/panic.rs b/tests/integration/panic.rs index e49df922849..5b3e4d8db31 100644 --- a/tests/integration/panic.rs +++ b/tests/integration/panic.rs @@ -186,11 +186,19 @@ impl RustIrDatabase for MockDatabase { unimplemented!() } - fn closure_datum( + fn closure_inputs_and_output( &self, closure_id: ClosureId, substs: &Substitution, - ) -> Arc> { + ) -> Binders> { + unimplemented!() + } + + fn closure_kind( + &self, + closure_id: ClosureId, + substs: &Substitution, + ) -> ClosureKind { unimplemented!() } @@ -201,6 +209,14 @@ impl RustIrDatabase for MockDatabase { ) -> Binders> { unimplemented!() } + + fn closure_fn_substitution( + &self, + closure_id: ClosureId, + substs: &Substitution, + ) -> Substitution { + unimplemented!() + } } fn prepare_goal() -> UCanonical>> {