diff --git a/chalk-integration/src/db.rs b/chalk-integration/src/db.rs index a67a3d2e8a2..5d2d69110f0 100644 --- a/chalk-integration/src/db.rs +++ b/chalk-integration/src/db.rs @@ -10,6 +10,7 @@ use chalk_ir::AssocTypeId; use chalk_ir::Canonical; use chalk_ir::ConstrainedSubst; use chalk_ir::Environment; +use chalk_ir::FnDefId; use chalk_ir::GenericArg; use chalk_ir::Goal; use chalk_ir::ImplId; @@ -22,6 +23,7 @@ use chalk_rust_ir::AdtDatum; use chalk_rust_ir::AssociatedTyDatum; use chalk_rust_ir::AssociatedTyValue; use chalk_rust_ir::AssociatedTyValueId; +use chalk_rust_ir::FnDefDatum; use chalk_rust_ir::ImplDatum; use chalk_rust_ir::OpaqueTyDatum; use chalk_rust_ir::TraitDatum; @@ -114,6 +116,10 @@ impl RustIrDatabase for ChalkDatabase { self.program_ir().unwrap().adt_datum(id) } + fn fn_def_datum(&self, id: FnDefId) -> Arc> { + self.program_ir().unwrap().fn_def_datum(id) + } + fn impls_for_trait( &self, trait_id: TraitId, diff --git a/chalk-integration/src/lib.rs b/chalk-integration/src/lib.rs index df6a48a576d..702eff51f2b 100644 --- a/chalk-integration/src/lib.rs +++ b/chalk-integration/src/lib.rs @@ -22,6 +22,7 @@ pub use interner::{Identifier, RawId}; #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum TypeSort { Struct, + FnDef, Trait, Opaque, } diff --git a/chalk-integration/src/lowering.rs b/chalk-integration/src/lowering.rs index c8c4513a5b3..4b1bcc4932e 100644 --- a/chalk-integration/src/lowering.rs +++ b/chalk-integration/src/lowering.rs @@ -2,7 +2,7 @@ use crate::interner::ChalkIr; use chalk_ir::cast::{Cast, Caster}; use chalk_ir::interner::HasInterner; use chalk_ir::{ - self, AdtId, AssocTypeId, BoundVar, ClausePriority, DebruijnIndex, ImplId, OpaqueTyId, + self, AdtId, AssocTypeId, BoundVar, ClausePriority, DebruijnIndex, FnDefId, ImplId, OpaqueTyId, QuantifiedWhereClauses, Substitution, ToGenericArg, TraitId, }; use chalk_parse::ast::*; @@ -19,9 +19,11 @@ use crate::program::Program as LoweredProgram; use crate::{Identifier as Ident, RawId, TypeKind, TypeSort}; type AdtIds = BTreeMap>; +type FnDefIds = BTreeMap>; type TraitIds = BTreeMap>; type OpaqueTyIds = BTreeMap>; type AdtKinds = BTreeMap, TypeKind>; +type FnDefKinds = BTreeMap, TypeKind>; type TraitKinds = BTreeMap, TypeKind>; type AssociatedTyLookups = BTreeMap<(chalk_ir::TraitId, Ident), AssociatedTyLookup>; type AssociatedTyValueIds = @@ -36,6 +38,8 @@ pub type LowerResult = Result; struct Env<'k> { adt_ids: &'k AdtIds, adt_kinds: &'k AdtKinds, + fn_def_ids: &'k FnDefIds, + fn_def_kinds: &'k FnDefKinds, trait_ids: &'k TraitIds, trait_kinds: &'k TraitKinds, opaque_ty_ids: &'k OpaqueTyIds, @@ -73,6 +77,7 @@ struct AssociatedTyLookup { enum TypeLookup { Adt(chalk_ir::AdtId), GenericArg(BoundVar), + FnDef(chalk_ir::FnDefId), Opaque(chalk_ir::OpaqueTyId), } @@ -96,6 +101,10 @@ impl<'k> Env<'k> { return Ok(TypeLookup::Adt(*id)); } + if let Some(id) = self.fn_def_ids.get(&name.str) { + return Ok(TypeLookup::FnDef(*id)); + } + if let Some(id) = self.opaque_ty_ids.get(&name.str) { return Ok(TypeLookup::Opaque(*id)); } @@ -140,6 +149,10 @@ impl<'k> Env<'k> { &self.adt_kinds[&id] } + fn fn_def_kind(&self, id: chalk_ir::FnDefId) -> &TypeKind { + &self.fn_def_kinds[&id] + } + fn trait_kind(&self, id: chalk_ir::TraitId) -> &TypeKind { &self.trait_kinds[&id] } @@ -240,9 +253,11 @@ impl LowerProgram for Program { } let mut adt_ids = BTreeMap::new(); + let mut fn_def_ids = BTreeMap::new(); let mut trait_ids = BTreeMap::new(); let mut opaque_ty_ids = BTreeMap::new(); let mut adt_kinds = BTreeMap::new(); + let mut fn_def_kinds = BTreeMap::new(); let mut trait_kinds = BTreeMap::new(); let mut opaque_ty_kinds = BTreeMap::new(); let mut object_safe_traits = HashSet::new(); @@ -254,6 +269,12 @@ impl LowerProgram for Program { adt_ids.insert(type_kind.name.clone(), id); adt_kinds.insert(id, type_kind); } + Item::FnDefn(defn) => { + let type_kind = defn.lower_type_kind()?; + let id = FnDefId(raw_id); + fn_def_ids.insert(type_kind.name.clone(), id); + fn_def_kinds.insert(id, type_kind); + } Item::TraitDefn(defn) => { let type_kind = defn.lower_type_kind()?; let id = TraitId(raw_id); @@ -276,6 +297,7 @@ impl LowerProgram for Program { } let mut adt_data = BTreeMap::new(); + let mut fn_def_data = BTreeMap::new(); let mut trait_data = BTreeMap::new(); let mut well_known_traits = BTreeMap::new(); let mut impl_data = BTreeMap::new(); @@ -287,6 +309,8 @@ impl LowerProgram for Program { let empty_env = Env { adt_ids: &adt_ids, adt_kinds: &adt_kinds, + fn_def_ids: &fn_def_ids, + fn_def_kinds: &fn_def_kinds, trait_ids: &trait_ids, trait_kinds: &trait_kinds, opaque_ty_ids: &opaque_ty_ids, @@ -299,6 +323,13 @@ impl LowerProgram for Program { let adt_id = AdtId(raw_id); adt_data.insert(adt_id, Arc::new(d.lower_adt(adt_id, &empty_env)?)); } + Item::FnDefn(ref defn) => { + let fn_def_id = FnDefId(raw_id); + fn_def_data.insert( + fn_def_id, + Arc::new(defn.lower_fn_def(fn_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)?; @@ -454,10 +485,13 @@ impl LowerProgram for Program { let program = LoweredProgram { adt_ids, + fn_def_ids, trait_ids, adt_kinds, + fn_def_kinds, trait_kinds, adt_data, + fn_def_data, trait_data, well_known_traits, impl_data, @@ -538,6 +572,16 @@ impl LowerParameterMap for StructDefn { } } +impl LowerParameterMap for FnDefn { + fn synthetic_parameters(&self) -> Option { + None + } + + fn declared_parameters(&self) -> &[VariableKind] { + &self.variable_kinds + } +} + impl LowerParameterMap for Impl { fn synthetic_parameters(&self) -> Option { None @@ -631,6 +675,26 @@ impl LowerTypeKind for StructDefn { } } +impl LowerTypeKind for FnDefn { + fn lower_type_kind(&self) -> LowerResult { + let interner = &ChalkIr; + Ok(TypeKind { + sort: TypeSort::FnDef, + name: self.name.str.clone(), + binders: chalk_ir::Binders::new( + chalk_ir::VariableKinds::from(interner, self.all_parameters().anonymize()), + crate::Unit, + ), + }) + } +} + +impl LowerWhereClauses for FnDefn { + fn where_clauses(&self) -> &[QuantifiedWhereClause] { + &self.where_clauses + } +} + impl LowerWhereClauses for StructDefn { fn where_clauses(&self) -> &[QuantifiedWhereClause] { &self.where_clauses @@ -850,6 +914,39 @@ impl LowerAdtDefn for StructDefn { } } +trait LowerFnDefn { + fn lower_fn_def( + &self, + fn_def_id: chalk_ir::FnDefId, + env: &Env, + ) -> LowerResult>; +} + +impl LowerFnDefn for FnDefn { + fn lower_fn_def( + &self, + fn_def_id: chalk_ir::FnDefId, + 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)?; + + Ok(rust_ir::FnDefDatumBound { + argument_types: args?, + where_clauses, + return_type, + }) + })?; + + Ok(rust_ir::FnDefDatum { + id: fn_def_id, + binders, + }) + } +} + trait LowerTraitRef { fn lower(&self, env: &Env) -> LowerResult>; } @@ -1121,6 +1218,22 @@ impl LowerTy for Ty { } } TypeLookup::GenericArg(d) => Ok(chalk_ir::TyData::BoundVar(d).intern(interner)), + TypeLookup::FnDef(id) => { + let k = env.fn_def_kind(id); + if k.binders.len(interner) > 0 { + Err(RustIrError::IncorrectNumberOfTypeParameters { + identifier: name.clone(), + expected: k.binders.len(interner), + actual: 0, + }) + } else { + Ok(chalk_ir::TyData::Function(chalk_ir::Fn { + num_binders: k.binders.len(interner), + substitution: chalk_ir::Substitution::empty(interner), + }) + .intern(interner)) + } + } TypeLookup::Opaque(id) => Ok(chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque( chalk_ir::OpaqueTy { opaque_ty_id: id, @@ -1157,14 +1270,14 @@ impl LowerTy for Ty { .intern(interner)), Ty::Apply { name, ref args } => { - let id = match env.lookup_type(name)? { - TypeLookup::Adt(id) => id, + let (id, k) = match env.lookup_type(name)? { + TypeLookup::Adt(id) => (id.0, env.adt_kind(id)), TypeLookup::GenericArg(_) | TypeLookup::Opaque(_) => { Err(RustIrError::CannotApplyTypeParameter(name.clone()))? } + TypeLookup::FnDef(id) => (id.0, env.fn_def_kind(id)), }; - let k = env.adt_kind(id); if k.binders.len(interner) != args.len() { Err(RustIrError::IncorrectNumberOfTypeParameters { identifier: name.clone(), @@ -1188,11 +1301,18 @@ impl LowerTy for Ty { } } - Ok(chalk_ir::TyData::Apply(chalk_ir::ApplicationTy { - name: chalk_ir::TypeName::Adt(id), - substitution, - }) - .intern(interner)) + match k.sort { + TypeSort::FnDef => Ok(chalk_ir::TyData::Apply(chalk_ir::ApplicationTy { + name: chalk_ir::TypeName::FnDef(FnDefId(id)), + substitution, + }) + .intern(interner)), + _ => Ok(chalk_ir::TyData::Apply(chalk_ir::ApplicationTy { + name: chalk_ir::TypeName::Adt(AdtId(id)), + substitution, + }) + .intern(interner)), + } } Ty::Projection { ref proj } => Ok(chalk_ir::TyData::Alias( @@ -1485,9 +1605,11 @@ impl LowerGoal for Goal { let env = Env { adt_ids: &program.adt_ids, + fn_def_ids: &program.fn_def_ids, trait_ids: &program.trait_ids, opaque_ty_ids: &program.opaque_ty_ids, adt_kinds: &program.adt_kinds, + fn_def_kinds: &program.fn_def_kinds, trait_kinds: &program.trait_kinds, associated_ty_lookups: &associated_ty_lookups, parameter_map: BTreeMap::new(), diff --git a/chalk-integration/src/program.rs b/chalk-integration/src/program.rs index 773d92de16d..f11b285312f 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, GenericArg, Goal, Goals, - ImplId, Lifetime, OpaqueTy, OpaqueTyId, ProgramClause, ProgramClauseImplication, + debug::SeparatorTraitRef, AdtId, AliasTy, ApplicationTy, AssocTypeId, FnDefId, GenericArg, + Goal, Goals, ImplId, Lifetime, OpaqueTy, OpaqueTyId, ProgramClause, ProgramClauseImplication, ProgramClauses, ProjectionTy, Substitution, TraitId, Ty, }; use chalk_rust_ir::{ - AdtDatum, AssociatedTyDatum, AssociatedTyValue, AssociatedTyValueId, ImplDatum, ImplType, - OpaqueTyDatum, TraitDatum, WellKnownTrait, + AdtDatum, AssociatedTyDatum, AssociatedTyValue, AssociatedTyValueId, FnDefDatum, ImplDatum, + ImplType, OpaqueTyDatum, TraitDatum, WellKnownTrait, }; use chalk_solve::split::Split; use chalk_solve::RustIrDatabase; @@ -25,6 +25,10 @@ pub struct Program { /// For each ADT: pub adt_kinds: BTreeMap, TypeKind>, + pub fn_def_ids: BTreeMap>, + + pub fn_def_kinds: BTreeMap, TypeKind>, + /// From trait name to item-id. Used during lowering only. pub trait_ids: BTreeMap>, @@ -34,6 +38,8 @@ pub struct Program { /// For each ADT: pub adt_data: BTreeMap, Arc>>, + pub fn_def_data: BTreeMap, Arc>>, + /// For each impl: pub impl_data: BTreeMap, Arc>>, @@ -334,6 +340,10 @@ impl RustIrDatabase for Program { self.adt_data[&id].clone() } + fn fn_def_datum(&self, id: FnDefId) -> Arc> { + self.fn_def_data[&id].clone() + } + fn impls_for_trait( &self, trait_id: TraitId, diff --git a/chalk-ir/src/cast.rs b/chalk-ir/src/cast.rs index 390b120aa58..cb2a614c8d0 100644 --- a/chalk-ir/src/cast.rs +++ b/chalk-ir/src/cast.rs @@ -303,6 +303,15 @@ where } } +impl CastTo> for FnDefId +where + I: Interner, +{ + fn cast_to(self, _interner: &I) -> TypeName { + TypeName::FnDef(self) + } +} + impl CastTo for &T where T: Clone + HasInterner, diff --git a/chalk-ir/src/debug.rs b/chalk-ir/src/debug.rs index a3f97a23f6c..73afd7ceafb 100644 --- a/chalk-ir/src/debug.rs +++ b/chalk-ir/src/debug.rs @@ -21,6 +21,12 @@ impl Debug for AssocTypeId { } } +impl Debug for FnDefId { + fn fmt(&self, fmt: &mut Formatter<'_>) -> std::fmt::Result { + I::debug_fn_def_id(*self, fmt).unwrap_or_else(|| write!(fmt, "FnDefId({:?})", 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)) @@ -146,6 +152,7 @@ impl Debug for TypeName { TypeName::Tuple(arity) => write!(fmt, "{:?}", arity), TypeName::OpaqueType(opaque_ty) => write!(fmt, "!{:?}", opaque_ty), TypeName::Slice => write!(fmt, "{{slice}}"), + TypeName::FnDef(fn_def) => write!(fmt, "{:?}", fn_def), TypeName::Raw(mutability) => write!(fmt, "{:?}", mutability), TypeName::Ref(mutability) => write!(fmt, "{:?}", mutability), TypeName::Error => write!(fmt, "{{error}}"), diff --git a/chalk-ir/src/fold/boring_impls.rs b/chalk-ir/src/fold/boring_impls.rs index bc001ac1f9b..40778e755e5 100644 --- a/chalk-ir/src/fold/boring_impls.rs +++ b/chalk-ir/src/fold/boring_impls.rs @@ -281,6 +281,7 @@ id_fold!(AdtId, transfer_adt_id); id_fold!(TraitId); id_fold!(AssocTypeId); id_fold!(OpaqueTyId); +id_fold!(FnDefId); impl> SuperFold for ProgramClauseData { fn super_fold_with<'i>( diff --git a/chalk-ir/src/interner.rs b/chalk-ir/src/interner.rs index 8dfe3c049b7..de114a8aac6 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::FnDefId; use crate::GenericArg; use crate::GenericArgData; use crate::Goal; @@ -206,6 +207,14 @@ pub trait Interner: Debug + Copy + Eq + Ord + Hash { None } + #[allow(unused_variables)] + fn debug_fn_def_id( + fn_def_id: FnDefId, + 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 ae51665bb99..3b805630d30 100644 --- a/chalk-ir/src/lib.rs +++ b/chalk-ir/src/lib.rs @@ -169,6 +169,9 @@ pub enum TypeName { /// a placeholder for opaque types like `impl Trait` OpaqueType(OpaqueTyId), + /// a function definition + FnDef(FnDefId), + /// the string primitive type Str, @@ -237,6 +240,9 @@ pub struct AssocTypeId(pub I::DefId); #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct OpaqueTyId(pub I::DefId); +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct FnDefId(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 b5a6b12a428..6ede102f013 100644 --- a/chalk-ir/src/visit/boring_impls.rs +++ b/chalk-ir/src/visit/boring_impls.rs @@ -5,8 +5,8 @@ //! The more interesting impls of `Visit` remain in the `visit` module. use crate::{ - AdtId, AssocTypeId, ClausePriority, DebruijnIndex, FloatTy, GenericArg, Goals, ImplId, IntTy, - Interner, Mutability, OpaqueTyId, PlaceholderIndex, ProgramClause, ProgramClauseData, + AdtId, AssocTypeId, ClausePriority, DebruijnIndex, FloatTy, FnDefId, GenericArg, Goals, ImplId, + IntTy, Interner, Mutability, OpaqueTyId, PlaceholderIndex, ProgramClause, ProgramClauseData, ProgramClauses, QuantifiedWhereClauses, QuantifierKind, Scalar, Substitution, SuperVisit, TraitId, UintTy, UniverseIndex, Visit, VisitResult, Visitor, }; @@ -236,6 +236,7 @@ id_visit!(AdtId); id_visit!(TraitId); id_visit!(OpaqueTyId); id_visit!(AssocTypeId); +id_visit!(FnDefId); impl SuperVisit for ProgramClause { fn super_visit_with<'i, R: VisitResult>( diff --git a/chalk-parse/src/ast.rs b/chalk-parse/src/ast.rs index 310d40b299b..c72c6cbfe1d 100644 --- a/chalk-parse/src/ast.rs +++ b/chalk-parse/src/ast.rs @@ -21,6 +21,7 @@ pub struct Program { #[derive(Clone, PartialEq, Eq, Debug)] pub enum Item { StructDefn(StructDefn), + FnDefn(FnDefn), TraitDefn(TraitDefn), OpaqueTyDefn(OpaqueTyDefn), Impl(Impl), @@ -42,6 +43,15 @@ pub struct StructFlags { pub fundamental: bool, } +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct FnDefn { + pub name: Identifier, + pub variable_kinds: Vec, + pub where_clauses: Vec, + pub argument_types: Vec, + pub return_type: Ty, +} + #[derive(Clone, PartialEq, Eq, Debug)] pub struct TraitDefn { pub name: Identifier, diff --git a/chalk-parse/src/parser.lalrpop b/chalk-parse/src/parser.lalrpop index edcea0d0315..371450bdc11 100644 --- a/chalk-parse/src/parser.lalrpop +++ b/chalk-parse/src/parser.lalrpop @@ -14,6 +14,7 @@ Items: Vec = { Item: Option = { Comment => None, StructDefn => Some(Item::StructDefn(<>)), + FnDefn => Some(Item::FnDefn(<>)), TraitDefn => Some(Item::TraitDefn(<>)), OpaqueTyDefn => Some(Item::OpaqueTyDefn(<>)), Impl => Some(Item::Impl(<>)), @@ -67,6 +68,30 @@ StructDefn: StructDefn = { } }; +FnReturn: Ty = { + "->" => ty, +}; + +FnDefn: FnDefn = { + "fn" >"(" ")" + ";" => FnDefn + { + name: n, + variable_kinds: p, + where_clauses: w, + argument_types: args, + return_type: ret_ty.unwrap_or_else(|| Ty::Tuple { types: Vec::new() }), + } +}; + +FnArg: Ty = { + Id ":" => arg_ty +}; + +FnArgs: Vec = { + > +}; + TraitDefn: TraitDefn = { "trait" > "{" "}" => TraitDefn diff --git a/chalk-rust-ir/src/lib.rs b/chalk-rust-ir/src/lib.rs index 85b4b0ec5a0..1d3b9b47f1f 100644 --- a/chalk-rust-ir/src/lib.rs +++ b/chalk-rust-ir/src/lib.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, 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; @@ -103,6 +103,49 @@ pub struct AdtFlags { pub fundamental: bool, } +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +/// A rust intermediate represention (rust_ir) of a function definition/declaration. +/// For example, in the following rust code: +/// +/// ```ignore +/// fn foo() -> i32 where T: Eq; +/// ``` +/// +/// This would represent the declaration of `foo`. +/// +/// Note this is distinct from a function pointer, which points to +/// a function with a given type signature, whereas this represents +/// a specific function definition. +pub struct FnDefDatum { + pub id: FnDefId, + pub binders: Binders>, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, Fold, HasInterner)] +/// Represents the bounds on a `FnDefDatum`, including +/// the function definition's type signature and where clauses. +pub struct FnDefDatumBound { + /// Types of the function's arguments + /// ```ignore + /// fn foo(bar: i32, baz: T); + /// ^^^ ^ + /// ``` + /// + pub argument_types: Vec>, + /// Return type of the function + /// ```ignore + /// fn foo() -> i32; + /// ^^^ + /// ``` + pub return_type: Ty, + /// Where clauses defined on the function + /// ```ignore + /// fn foo() where T: Eq; + /// ^^^^^^^^^^^ + /// ``` + pub where_clauses: Vec>, +} + #[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/chalk-solve/src/clauses.rs b/chalk-solve/src/clauses.rs index 921fdfbebf3..35f08613092 100644 --- a/chalk-solve/src/clauses.rs +++ b/chalk-solve/src/clauses.rs @@ -412,6 +412,10 @@ fn match_type_name( TypeName::Slice => builder.push_fact(WellFormed::Ty(application.clone().intern(interner))), TypeName::Raw(_) => builder.push_fact(WellFormed::Ty(application.clone().intern(interner))), TypeName::Ref(_) => builder.push_fact(WellFormed::Ty(application.clone().intern(interner))), + TypeName::FnDef(fn_def_id) => builder + .db + .fn_def_datum(fn_def_id) + .to_program_clauses(builder), } } diff --git a/chalk-solve/src/clauses/program_clauses.rs b/chalk-solve/src/clauses/program_clauses.rs index ffe5355c7e2..e7349654729 100644 --- a/chalk-solve/src/clauses/program_clauses.rs +++ b/chalk-solve/src/clauses/program_clauses.rs @@ -1,6 +1,6 @@ use crate::clauses::builder::ClauseBuilder; use crate::split::Split; -use chalk_ir::cast::{Cast, Caster}; +use chalk_ir::cast::{Cast, CastTo, Caster}; use chalk_ir::interner::Interner; use chalk_ir::*; use chalk_rust_ir::*; @@ -180,6 +180,129 @@ impl ToProgramClauses for OpaqueTyDatum { } } +fn application_ty( + builder: &mut ClauseBuilder<'_, I>, + type_name: impl CastTo>, +) -> ApplicationTy { + let interner = builder.interner(); + ApplicationTy { + name: type_name.cast(interner), + substitution: builder.substitution_in_scope(), + } +} + +/// Generates the "well-formed" program clauses for an applicative type +/// with the name `type_name`. For example, given a struct definition: +/// +/// ```ignore +/// struct Foo { } +/// ``` +/// +/// we would generate the clause: +/// +/// ```notrust +/// forall { +/// WF(Foo) :- WF(T: Eq). +/// } +/// ``` +/// +/// # Parameters +/// - builder -- the clause builder. We assume all the generic types from `Foo` are in scope +/// - type_name -- in our example above, the name `Foo` +/// - where_clauses -- the list of where clauses declared on the type (`T: Eq`, in our example) +fn well_formed_program_clauses<'a, I, Wc>( + builder: &'a mut ClauseBuilder<'_, I>, + type_name: impl CastTo>, + where_clauses: Wc, +) where + I: Interner, + Wc: Iterator>, +{ + let interner = builder.interner(); + let appl_ty = application_ty(builder, type_name); + let ty = appl_ty.clone().intern(interner); + builder.push_clause( + WellFormed::Ty(ty.clone()), + where_clauses + .cloned() + .map(|qwc| qwc.into_well_formed_goal(interner)), + ); +} + +/// Generates the "fully visible" program clauses for an applicative type +/// with the name `type_name`. For example, given a struct definition: +/// +/// ```ignore +/// struct Foo { } +/// ``` +/// +/// we would generate the clause: +/// +/// ```notrust +/// forall { +/// IsFullyVisible(Foo) :- IsFullyVisible(T). +/// } +/// ``` +/// +/// # Parameters +/// +/// - builder -- the clause builder. We assume all the generic types from `Foo` are in scope +/// - type_name -- in our example above, the name `Foo` +fn fully_visible_program_clauses<'a, I>( + builder: &'a mut ClauseBuilder<'_, I>, + type_name: impl CastTo>, +) where + I: Interner, +{ + let interner = builder.interner(); + let appl_ty = application_ty(builder, type_name); + let ty = appl_ty.clone().intern(interner); + builder.push_clause( + DomainGoal::IsFullyVisible(ty.clone()), + appl_ty + .type_parameters(interner) + .map(|typ| DomainGoal::IsFullyVisible(typ).cast::>(interner)), + ); +} + +/// Generates the "implied bounds" clauses for an applicative +/// type with the name `type_name`. For example, if `type_name` +/// represents a struct `S` that is declared like: +/// +/// ```ignore +/// struct S where T: Eq { } +/// ``` +/// +/// then we would generate the rule: +/// +/// ```notrust +/// FromEnv(T: Eq) :- FromEnv(S) +/// ``` +/// +/// # Parameters +/// +/// - builder -- the clause builder. We assume all the generic types from `S` are in scope. +/// - type_name -- in our example above, the name `S` +/// - where_clauses -- the list of where clauses declared on the type (`T: Eq`, in our example). +fn implied_bounds_program_clauses<'a, I, Wc>( + builder: &'a mut ClauseBuilder<'_, I>, + type_name: impl CastTo>, + where_clauses: Wc, +) where + I: Interner, + Wc: Iterator>, +{ + let interner = builder.interner(); + let appl_ty = application_ty(builder, type_name); + let ty = appl_ty.clone().intern(interner); + + for qwc in where_clauses { + builder.push_binders(&qwc, |builder, wc| { + builder.push_clause(wc.into_from_env_goal(interner), Some(ty.clone().from_env())); + }); + } +} + impl ToProgramClauses for AdtDatum { /// Given the following type definition: `struct Foo { }`, generate: /// @@ -234,33 +357,17 @@ impl ToProgramClauses for AdtDatum { let interner = builder.interner(); let binders = self.binders.map_ref(|b| &b.where_clauses); + let id = self.id; + builder.push_binders(&binders, |builder, where_clauses| { - let self_appl_ty = &ApplicationTy { - name: self.id.cast(interner), - substitution: builder.substitution_in_scope(), - }; - let self_ty = self_appl_ty.clone().intern(interner); + well_formed_program_clauses(builder, id, where_clauses.iter()); - // forall { - // WF(Foo) :- WF(T: Eq). - // } - builder.push_clause( - WellFormed::Ty(self_ty.clone()), - where_clauses - .iter() - .cloned() - .map(|qwc| qwc.into_well_formed_goal(interner)), - ); + implied_bounds_program_clauses(builder, id, where_clauses.iter()); - // forall { - // IsFullyVisible(Foo) :- IsFullyVisible(T). - // } - builder.push_clause( - DomainGoal::IsFullyVisible(self_ty.clone()), - self_appl_ty - .type_parameters(interner) - .map(|ty| DomainGoal::IsFullyVisible(ty).cast::>(interner)), - ); + fully_visible_program_clauses(builder, id); + + let self_appl_ty = application_ty(builder, id); + let self_ty = self_appl_ty.clone().intern(interner); // Fundamental types often have rules in the form of: // Goal(FundamentalType) :- Goal(T) @@ -313,26 +420,40 @@ impl ToProgramClauses for AdtDatum { if self.flags.fundamental { fundamental_rule!(DownstreamType); } + }); + } +} - for qwc in where_clauses { - // Generate implied bounds rules. We have to push the binders from the where-clauses - // too -- e.g., if we had `struct Foo Bar<&'a i32>>`, we would - // create a reverse rule like: - // - // ```notrust - // forall { FromEnv(T: Bar<&'a i32>) :- FromEnv(Foo) } - // ``` - // - // In other words, you can assume `T: Bar<&'a i32>` - // for any `'a` *if* you are assuming that `Foo` is - // well formed. - builder.push_binders(&qwc, |builder, wc| { - builder.push_clause( - wc.into_from_env_goal(interner), - Some(self_ty.clone().from_env()), - ); - }); - } +impl ToProgramClauses for FnDefDatum { + /// Given the following function definition: `fn bar() where T: Eq`, generate: + /// + /// ```notrust + /// -- Rule WellFormed-Type + /// forall { + /// WF(bar) :- WF(T: Eq) + /// } + /// + /// -- Rule Implied-Bound-From-Type + /// forall { + /// FromEnv(T: Eq) :- FromEnv(bar). + /// } + /// + /// forall { + /// IsFullyVisible(bar) :- IsFullyVisible(T). + /// } + /// ``` + fn to_program_clauses(&self, builder: &mut ClauseBuilder<'_, I>) { + debug_heading!("FnDatum::to_program_clauses(self={:?})", self); + + let binders = self.binders.map_ref(|b| &b.where_clauses); + let id = self.id; + + builder.push_binders(&binders, |builder, where_clauses| { + well_formed_program_clauses(builder, id, where_clauses.iter()); + + implied_bounds_program_clauses(builder, id, where_clauses.iter()); + + fully_visible_program_clauses(builder, id); }); } } diff --git a/chalk-solve/src/lib.rs b/chalk-solve/src/lib.rs index 6be11c40e75..9747e6da4e8 100644 --- a/chalk-solve/src/lib.rs +++ b/chalk-solve/src/lib.rs @@ -38,6 +38,8 @@ pub trait RustIrDatabase: Debug { /// Returns the datum for the impl with the given id. fn adt_datum(&self, adt_id: AdtId) -> Arc>; + fn fn_def_datum(&self, fn_def_id: FnDefId) -> Arc>; + /// Returns the datum for the impl with the given id. fn impl_datum(&self, impl_id: ImplId) -> Arc>; diff --git a/tests/lowering/mod.rs b/tests/lowering/mod.rs index 040ab7c1ddc..60f370ee8ed 100644 --- a/tests/lowering/mod.rs +++ b/tests/lowering/mod.rs @@ -559,3 +559,28 @@ fn slices() { } } } + +#[test] +fn fn_defs() { + lowering_success! { + program { + trait Quux { } + + fn foo<'a, T>(bar: T, baz: &'a mut T) -> u32 + where T: Quux; + } + } + + lowering_error! { + program { + trait Quux { } + + fn foo(bar: TT) -> T + where T: Quux; + } + + error_msg { + "invalid type name `TT`" + } + } +} diff --git a/tests/test/functions.rs b/tests/test/functions.rs index d58428aa500..3a6d6a83c50 100644 --- a/tests/test/functions.rs +++ b/tests/test/functions.rs @@ -23,3 +23,62 @@ fn functions_are_sized() { } } } + +#[test] +fn fn_defs() { + test! { + program { + trait Foo { } + + struct Bar { } + + struct Xyzzy { } + impl Foo for Xyzzy { } + + fn baz(quux: T) -> T + where T: Foo; + + fn garply(thud: i32) -> i32; + } + + goal { + WellFormed(baz) + } yields { + "No possible solution" + } + + goal { + WellFormed(baz) + } yields { + "Unique" + } + + goal { + WellFormed(garply) + } yields { + "Unique" + } + + } +} + +#[test] +fn fn_def_implied_bounds_from_env() { + test! { + program { + trait Foo { } + + struct Bar { } + impl Foo for Bar { } + + fn baz() where T: Foo; + } + goal { + if (FromEnv(baz)) { + Bar: Foo + } + } yields { + "Unique" + } + } +}