From 457b8bbf69f2566ffbafa48a6f14d66f573a0c2c Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Mon, 15 Jun 2020 19:17:52 -0400 Subject: [PATCH 1/4] Implement generators `GeneratorDatum` and `GeneratorWitnessDatum` are introduced, which mirror `TyKind::Generator` and `TyKind::GeneratorWitness` in rustc. We handle auto traits for `GeneratorDatum` using the existing `constituent_types` method. For `GeneratorWitnessDatum`, we generate a `forall<'a, 'b, ... 'z>` goal to reflect the fact that our lifetimes are existentially bound. Unresolved questions: * The implementation of `GeneratorWitnessDatum` needs careful review - I'm not certain that I fully understand how `Binders` works. * The syntax implemented in `parser.lalrpop` can be bikesheeded. We might want to use something other than `for<>` for the witness types, to reflect that fact that we have existential rather than universal quantification. * The generator grammar only allows quantifying over liftimes for the witness types - however, this gets lowered to a `Binders`, which supports types and constants as well. Should we do anything else to ensure we never end up with types/consts where they're not expected? * I added a test to ensure that we treat the witness lifetimes as existentially quantified - for example, if we have `for<'a, 'b> Foo<'a, 'b>`, we should only be able to use an auto trait impl that is fully generic over lifetimes - e.g. `impl<'a> Send for Foo<'a, 'b>` will not be used. I *believe* that the test is showing this behavior - it ends up returning region constraints that require `'a` and `'b` to outlive each other. Since these are 'existential' lifetimes (which cannot be substituted), this is an unsatisfiable constraint. However, I'm not sure where we should be detecting that this is unsatisfiable. --- chalk-integration/src/db.rs | 18 ++- chalk-integration/src/lib.rs | 1 + chalk-integration/src/lowering.rs | 5 + chalk-integration/src/lowering/env.rs | 17 ++- .../src/lowering/program_lowerer.rs | 72 ++++++++++- chalk-integration/src/program.rs | 30 ++++- chalk-ir/src/cast.rs | 9 ++ chalk-ir/src/debug.rs | 9 ++ chalk-ir/src/fold/boring_impls.rs | 1 + chalk-ir/src/interner.rs | 16 +++ chalk-ir/src/lib.rs | 10 ++ chalk-ir/src/visit/boring_impls.rs | 3 +- chalk-parse/src/ast.rs | 13 ++ chalk-parse/src/parser.lalrpop | 18 +++ chalk-solve/src/clauses.rs | 112 +++++++++++++++++- .../src/clauses/builtin_traits/copy.rs | 2 + .../src/clauses/builtin_traits/sized.rs | 2 + chalk-solve/src/display.rs | 5 + chalk-solve/src/display/items.rs | 6 + chalk-solve/src/display/stub.rs | 15 ++- chalk-solve/src/display/ty.rs | 2 + chalk-solve/src/lib.rs | 9 ++ chalk-solve/src/logging_db.rs | 33 ++++++ chalk-solve/src/logging_db/id_collector.rs | 1 + chalk-solve/src/rust_ir.rs | 62 ++++++++++ tests/display/unique_names.rs | 12 ++ tests/integration/panic.rs | 11 ++ tests/test/generators.rs | 89 ++++++++++++++ tests/test/mod.rs | 1 + 29 files changed, 569 insertions(+), 15 deletions(-) create mode 100644 tests/test/generators.rs diff --git a/chalk-integration/src/db.rs b/chalk-integration/src/db.rs index 89175b389fe..04d2fd8a997 100644 --- a/chalk-integration/src/db.rs +++ b/chalk-integration/src/db.rs @@ -8,12 +8,13 @@ use crate::{ }; use chalk_ir::{ AdtId, ApplicationTy, AssocTypeId, Binders, Canonical, CanonicalVarKinds, ClosureId, - ConstrainedSubst, Environment, FnDefId, GenericArg, Goal, ImplId, InEnvironment, OpaqueTyId, - ProgramClause, ProgramClauses, Substitution, TraitId, Ty, UCanonical, + ConstrainedSubst, Environment, FnDefId, GeneratorId, GenericArg, Goal, ImplId, InEnvironment, + OpaqueTyId, ProgramClause, ProgramClauses, Substitution, TraitId, Ty, UCanonical, }; use chalk_solve::rust_ir::{ AdtDatum, AdtRepr, AssociatedTyDatum, AssociatedTyValue, AssociatedTyValueId, ClosureKind, - FnDefDatum, FnDefInputsAndOutputDatum, ImplDatum, OpaqueTyDatum, TraitDatum, WellKnownTrait, + FnDefDatum, FnDefInputsAndOutputDatum, GeneratorDatum, GeneratorWitnessDatum, ImplDatum, + OpaqueTyDatum, TraitDatum, WellKnownTrait, }; use chalk_solve::{RustIrDatabase, Solution, SubstitutionResult}; use salsa::Database; @@ -106,6 +107,17 @@ impl RustIrDatabase for ChalkDatabase { self.program_ir().unwrap().adt_datum(id) } + fn generator_datum(&self, id: GeneratorId) -> Arc> { + self.program_ir().unwrap().generator_datum(id) + } + + fn generator_witness_datum( + &self, + id: GeneratorId, + ) -> Arc> { + self.program_ir().unwrap().generator_witness_datum(id) + } + fn adt_repr(&self, id: AdtId) -> AdtRepr { self.program_ir().unwrap().adt_repr(id) } diff --git a/chalk-integration/src/lib.rs b/chalk-integration/src/lib.rs index 31edec67983..e6ccee8e40f 100644 --- a/chalk-integration/src/lib.rs +++ b/chalk-integration/src/lib.rs @@ -27,6 +27,7 @@ pub enum TypeSort { Closure, Trait, Opaque, + Generator, } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] diff --git a/chalk-integration/src/lowering.rs b/chalk-integration/src/lowering.rs index 324c25f68f9..277d6eb32cd 100644 --- a/chalk-integration/src/lowering.rs +++ b/chalk-integration/src/lowering.rs @@ -709,6 +709,9 @@ impl LowerWithEnv for Ty { TypeLookup::Opaque(id) => { (chalk_ir::TypeName::OpaqueType(id), env.opaque_kind(id)) } + TypeLookup::Generator(id) => { + (chalk_ir::TypeName::Generator(id), env.generator_kind(id)) + } TypeLookup::Foreign(_) | TypeLookup::Trait(_) => { panic!("Unexpected apply type") @@ -1070,6 +1073,8 @@ pub fn lower_goal(goal: &Goal, program: &LoweredProgram) -> LowerResult>; pub type FnDefIds = BTreeMap>; pub type ClosureIds = BTreeMap>; pub type TraitIds = BTreeMap>; +pub type GeneratorIds = BTreeMap>; pub type OpaqueTyIds = BTreeMap>; pub type AdtKinds = BTreeMap, TypeKind>; pub type FnDefKinds = BTreeMap, TypeKind>; @@ -22,6 +24,7 @@ pub type ClosureKinds = BTreeMap, TypeKind>; pub type TraitKinds = BTreeMap, TypeKind>; pub type AutoTraits = BTreeMap, bool>; pub type OpaqueTyKinds = BTreeMap, TypeKind>; +pub type GeneratorKinds = BTreeMap, TypeKind>; pub type AssociatedTyLookups = BTreeMap<(chalk_ir::TraitId, Ident), AssociatedTyLookup>; pub type AssociatedTyValueIds = BTreeMap<(chalk_ir::ImplId, Ident), AssociatedTyValueId>; @@ -46,6 +49,8 @@ pub struct Env<'k> { pub associated_ty_lookups: &'k AssociatedTyLookups, pub auto_traits: &'k AutoTraits, pub foreign_ty_ids: &'k ForeignIds, + pub generator_ids: &'k GeneratorIds, + pub generator_kinds: &'k GeneratorKinds, /// GenericArg identifiers are used as keys, therefore /// all identifiers in an environment must be unique (no shadowing). pub parameter_map: ParameterMap, @@ -78,6 +83,7 @@ pub enum TypeLookup<'k> { Opaque(OpaqueTyId), Foreign(ForeignDefId), Trait(TraitId), + Generator(GeneratorId), } impl Env<'_> { @@ -128,6 +134,9 @@ impl Env<'_> { Ok(TypeLookup::Closure(id)) => { apply(self.closure_kind(id), chalk_ir::TypeName::Closure(id)) } + Ok(TypeLookup::Generator(id)) => { + apply(self.generator_kind(id), chalk_ir::TypeName::Generator(id)) + } Ok(TypeLookup::Opaque(id)) => Ok(chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque( chalk_ir::OpaqueTy { opaque_ty_id: id, @@ -162,6 +171,8 @@ impl Env<'_> { Ok(TypeLookup::Foreign(*id)) } else if let Some(id) = self.trait_ids.get(&name.str) { Ok(TypeLookup::Trait(*id)) + } else if let Some(id) = self.generator_ids.get(&name.str) { + Ok(TypeLookup::Generator(*id)) } else { Err(RustIrError::NotStruct(name.clone())) } @@ -203,6 +214,10 @@ impl Env<'_> { &self.opaque_ty_kinds[&id] } + pub fn generator_kind(&self, id: chalk_ir::GeneratorId) -> &TypeKind { + &self.generator_kinds[&id] + } + pub fn lookup_associated_ty( &self, trait_id: TraitId, diff --git a/chalk-integration/src/lowering/program_lowerer.rs b/chalk-integration/src/lowering/program_lowerer.rs index 41c80ed6fd7..62aea00fddc 100644 --- a/chalk-integration/src/lowering/program_lowerer.rs +++ b/chalk-integration/src/lowering/program_lowerer.rs @@ -1,11 +1,12 @@ use chalk_ir::cast::Cast; use chalk_ir::{ - self, AdtId, AssocTypeId, BoundVar, ClosureId, DebruijnIndex, FnDefId, ForeignDefId, ImplId, - OpaqueTyId, TraitId, TyKind, VariableKinds, + self, AdtId, AssocTypeId, BoundVar, ClosureId, DebruijnIndex, FnDefId, ForeignDefId, + GeneratorId, ImplId, OpaqueTyId, TraitId, TyKind, VariableKinds, }; use chalk_parse::ast::*; use chalk_solve::rust_ir::{ - self, Anonymize, AssociatedTyValueId, OpaqueTyDatum, OpaqueTyDatumBound, + self, Anonymize, AssociatedTyValueId, GeneratorDatum, GeneratorInputOutputDatum, + GeneratorWitnessDatum, GeneratorWitnessExistential, OpaqueTyDatum, OpaqueTyDatumBound, }; use rust_ir::IntoWhereClauses; use std::collections::{BTreeMap, HashSet}; @@ -32,6 +33,8 @@ pub(super) struct ProgramLowerer { opaque_ty_ids: OpaqueTyIds, adt_kinds: AdtKinds, fn_def_kinds: FnDefKinds, + generator_ids: GeneratorIds, + generator_kinds: GeneratorKinds, closure_kinds: ClosureKinds, trait_kinds: TraitKinds, opaque_ty_kinds: OpaqueTyKinds, @@ -125,6 +128,11 @@ impl ProgramLowerer { self.foreign_ty_ids .insert(ident.str.clone(), ForeignDefId(raw_id)); } + Item::GeneratorDefn(defn) => { + let id = GeneratorId(raw_id); + self.generator_ids.insert(defn.name.str.clone(), id); + self.generator_kinds.insert(id, defn.lower_type_kind()?); + } Item::Impl(_) => continue, Item::Clause(_) => continue, }; @@ -145,6 +153,8 @@ impl ProgramLowerer { let mut associated_ty_data = BTreeMap::new(); let mut associated_ty_values = BTreeMap::new(); let mut opaque_ty_data = BTreeMap::new(); + let mut generator_data = BTreeMap::new(); + let mut generator_witness_data = BTreeMap::new(); let mut hidden_opaque_types = BTreeMap::new(); let mut custom_clauses = Vec::new(); @@ -160,6 +170,8 @@ impl ProgramLowerer { trait_kinds: &self.trait_kinds, opaque_ty_ids: &self.opaque_ty_ids, opaque_ty_kinds: &self.opaque_ty_kinds, + generator_ids: &self.generator_ids, + generator_kinds: &self.generator_kinds, associated_ty_lookups: &self.associated_ty_lookups, parameter_map: BTreeMap::new(), auto_traits: &self.auto_traits, @@ -356,6 +368,51 @@ impl ProgramLowerer { ); } } + Item::GeneratorDefn(ref defn) => { + let variable_kinds = defn + .variable_kinds + .iter() + .map(|k| k.lower()) + .collect::>(); + + let witness_lifetimes = defn + .witness_lifetimes + .iter() + .map(|i| VariableKind::Lifetime(i.clone()).lower()) + .collect::>(); + + let input_output = empty_env.in_binders(variable_kinds.clone(), |env| { + let yield_type = defn.yield_ty.lower(&env)?; + let resume_type = defn.resume_ty.lower(&env)?; + let return_type = defn.return_ty.lower(&env)?; + let upvars: Result, _> = + defn.upvars.iter().map(|ty| ty.lower(&env)).collect(); + + Ok(GeneratorInputOutputDatum { + resume_type, + yield_type, + return_type, + upvars: upvars?, + }) + })?; + + let inner_types = empty_env.in_binders(variable_kinds, |env| { + let witnesses = env.in_binders(witness_lifetimes, |env| { + let witnesses: Result, _> = + defn.witness_types.iter().map(|ty| ty.lower(&env)).collect(); + witnesses + })?; + + Ok(GeneratorWitnessExistential { types: witnesses }) + })?; + + let generator_datum = GeneratorDatum { input_output }; + let generator_witness = GeneratorWitnessDatum { inner_types }; + + let id = self.generator_ids[&defn.name.str]; + generator_data.insert(id, Arc::new(generator_datum)); + generator_witness_data.insert(id, Arc::new(generator_witness)); + } Item::Foreign(_) => {} } } @@ -375,6 +432,10 @@ impl ProgramLowerer { fn_def_data, closure_inputs_and_output, closure_closure_kind, + generator_ids: self.generator_ids, + generator_kinds: self.generator_kinds, + generator_data, + generator_witness_data, trait_data, well_known_traits, impl_data, @@ -416,6 +477,11 @@ lower_type_kind!(AdtDefn, Adt, |defn: &AdtDefn| defn.all_parameters()); lower_type_kind!(FnDefn, FnDef, |defn: &FnDefn| defn.all_parameters()); lower_type_kind!(ClosureDefn, Closure, |defn: &ClosureDefn| defn .all_parameters()); +lower_type_kind!(GeneratorDefn, Generator, |defn: &GeneratorDefn| defn + .variable_kinds + .iter() + .map(|k| k.lower()) + .collect::>()); lower_type_kind!(TraitDefn, Trait, |defn: &TraitDefn| defn .variable_kinds .iter() diff --git a/chalk-integration/src/program.rs b/chalk-integration/src/program.rs index 76a3017754e..045c25a446d 100644 --- a/chalk-integration/src/program.rs +++ b/chalk-integration/src/program.rs @@ -4,14 +4,14 @@ use chalk_ir::could_match::CouldMatch; use chalk_ir::debug::Angle; use chalk_ir::{ debug::SeparatorTraitRef, AdtId, AliasTy, ApplicationTy, AssocTypeId, Binders, - CanonicalVarKinds, ClosureId, FnDefId, ForeignDefId, GenericArg, Goal, Goals, ImplId, Lifetime, - OpaqueTy, OpaqueTyId, ProgramClause, ProgramClauseImplication, ProgramClauses, ProjectionTy, - Substitution, TraitId, Ty, TyData, + CanonicalVarKinds, ClosureId, FnDefId, ForeignDefId, GeneratorId, GenericArg, Goal, Goals, + ImplId, Lifetime, OpaqueTy, OpaqueTyId, ProgramClause, ProgramClauseImplication, + ProgramClauses, ProjectionTy, Substitution, TraitId, Ty, TyData, }; use chalk_solve::rust_ir::{ AdtDatum, AdtRepr, AssociatedTyDatum, AssociatedTyValue, AssociatedTyValueId, ClosureKind, - FnDefDatum, FnDefInputsAndOutputDatum, ImplDatum, ImplType, OpaqueTyDatum, TraitDatum, - WellKnownTrait, + FnDefDatum, FnDefInputsAndOutputDatum, GeneratorDatum, GeneratorWitnessDatum, ImplDatum, + ImplType, OpaqueTyDatum, TraitDatum, WellKnownTrait, }; use chalk_solve::split::Split; use chalk_solve::RustIrDatabase; @@ -37,6 +37,15 @@ pub struct Program { pub closure_kinds: BTreeMap, TypeKind>, + /// For each generator + pub generator_ids: BTreeMap>, + + pub generator_kinds: BTreeMap, TypeKind>, + + pub generator_data: BTreeMap, Arc>>, + + pub generator_witness_data: BTreeMap, Arc>>, + /// From trait name to item-id. Used during lowering only. pub trait_ids: BTreeMap>, @@ -380,6 +389,17 @@ impl RustIrDatabase for Program { self.adt_data[&id].clone() } + fn generator_datum(&self, id: GeneratorId) -> Arc> { + self.generator_data[&id].clone() + } + + fn generator_witness_datum( + &self, + id: GeneratorId, + ) -> Arc> { + self.generator_witness_data[&id].clone() + } + fn adt_repr(&self, id: AdtId) -> AdtRepr { self.adt_reprs[&id] } diff --git a/chalk-ir/src/cast.rs b/chalk-ir/src/cast.rs index c363d3800b3..cff57213af8 100644 --- a/chalk-ir/src/cast.rs +++ b/chalk-ir/src/cast.rs @@ -315,6 +315,15 @@ where } } +impl CastTo> for GeneratorId +where + I: Interner, +{ + fn cast_to(self, _interner: &I) -> TypeName { + TypeName::Generator(self) + } +} + impl CastTo> for FnDefId where I: Interner, diff --git a/chalk-ir/src/debug.rs b/chalk-ir/src/debug.rs index 4d77dea862b..b4ed88790e4 100644 --- a/chalk-ir/src/debug.rs +++ b/chalk-ir/src/debug.rs @@ -35,6 +35,13 @@ impl Debug for ClosureId { } } +impl Debug for GeneratorId { + fn fmt(&self, fmt: &mut Formatter<'_>) -> std::fmt::Result { + I::debug_generator_id(*self, fmt) + .unwrap_or_else(|| write!(fmt, "GeneratorId({:?})", self.0)) + } +} + impl Debug for ForeignDefId { fn fmt(&self, fmt: &mut Formatter<'_>) -> std::fmt::Result { I::debug_foreign_def_id(*self, fmt) @@ -205,6 +212,8 @@ impl Debug for TypeName { TypeName::Never => write!(fmt, "Never"), TypeName::Array => write!(fmt, "{{array}}"), TypeName::Closure(id) => write!(fmt, "{{closure:{:?}}}", id), + TypeName::Generator(generator) => write!(fmt, "{:?}", generator), + TypeName::GeneratorWitness(witness) => write!(fmt, "{:?}", witness), TypeName::Foreign(foreign_ty) => write!(fmt, "{:?}", foreign_ty), TypeName::Error => write!(fmt, "{{error}}"), } diff --git a/chalk-ir/src/fold/boring_impls.rs b/chalk-ir/src/fold/boring_impls.rs index 9d6f835e1d6..b300c36974f 100644 --- a/chalk-ir/src/fold/boring_impls.rs +++ b/chalk-ir/src/fold/boring_impls.rs @@ -303,6 +303,7 @@ id_fold!(AssocTypeId); id_fold!(OpaqueTyId); id_fold!(FnDefId); id_fold!(ClosureId); +id_fold!(GeneratorId); id_fold!(ForeignDefId); impl> SuperFold for ProgramClauseData { diff --git a/chalk-ir/src/interner.rs b/chalk-ir/src/interner.rs index b4da73c53d6..eeacdd7b471 100644 --- a/chalk-ir/src/interner.rs +++ b/chalk-ir/src/interner.rs @@ -10,6 +10,7 @@ use crate::Constraint; use crate::Constraints; use crate::FnDefId; use crate::ForeignDefId; +use crate::GeneratorId; use crate::GenericArg; use crate::GenericArgData; use crate::Goal; @@ -259,6 +260,21 @@ pub trait Interner: Debug + Copy + Eq + Ord + Hash { /// Prints the debug representation of an alias. /// Returns `None` to fallback to the default debug output. #[allow(unused_variables)] + fn debug_generator_id( + generator_id: GeneratorId, + 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 + /// fully known types). + /// + /// Returns `None` to fallback to the default debug output (e.g., + /// if no info about current program is available from TLS). + #[allow(unused_variables)] fn debug_alias(alias: &AliasTy, fmt: &mut fmt::Formatter<'_>) -> Option { None } diff --git a/chalk-ir/src/lib.rs b/chalk-ir/src/lib.rs index 2464b1dc248..eaf328ee7ea 100644 --- a/chalk-ir/src/lib.rs +++ b/chalk-ir/src/lib.rs @@ -254,6 +254,12 @@ pub enum TypeName { /// A closure. Closure(ClosureId), + /// A generator. + Generator(GeneratorId), + + /// A generator witness. + GeneratorWitness(GeneratorId), + /// foreign types Foreign(ForeignDefId), @@ -366,6 +372,10 @@ pub struct FnDefId(pub I::DefId); #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct ClosureId(pub I::DefId); +/// Id for Rust generators. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct GeneratorId(pub I::DefId); + /// Id for foreign types. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct ForeignDefId(pub I::DefId); diff --git a/chalk-ir/src/visit/boring_impls.rs b/chalk-ir/src/visit/boring_impls.rs index 43e4cf16522..a34e9696966 100644 --- a/chalk-ir/src/visit/boring_impls.rs +++ b/chalk-ir/src/visit/boring_impls.rs @@ -6,7 +6,7 @@ use crate::{ AdtId, AssocTypeId, ClausePriority, ClosureId, Constraints, DebruijnIndex, FloatTy, FnDefId, - ForeignDefId, GenericArg, Goals, ImplId, IntTy, Interner, Mutability, OpaqueTyId, + ForeignDefId, GeneratorId, GenericArg, Goals, ImplId, IntTy, Interner, Mutability, OpaqueTyId, PlaceholderIndex, ProgramClause, ProgramClauses, QuantifiedWhereClauses, QuantifierKind, Safety, Scalar, Substitution, SuperVisit, TraitId, UintTy, UniverseIndex, Visit, VisitResult, Visitor, @@ -240,6 +240,7 @@ id_visit!(OpaqueTyId); id_visit!(AssocTypeId); id_visit!(FnDefId); id_visit!(ClosureId); +id_visit!(GeneratorId); id_visit!(ForeignDefId); impl SuperVisit for ProgramClause { diff --git a/chalk-parse/src/ast.rs b/chalk-parse/src/ast.rs index f80b8282af1..3734d0e6bff 100644 --- a/chalk-parse/src/ast.rs +++ b/chalk-parse/src/ast.rs @@ -25,6 +25,7 @@ pub enum Item { ClosureDefn(ClosureDefn), TraitDefn(TraitDefn), OpaqueTyDefn(OpaqueTyDefn), + GeneratorDefn(GeneratorDefn), Impl(Impl), Clause(Clause), Foreign(ForeignDefn), @@ -49,6 +50,18 @@ pub struct Variant { pub fields: Vec, } +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct GeneratorDefn { + pub name: Identifier, + pub variable_kinds: Vec, + pub upvars: Vec, + pub resume_ty: Ty, + pub yield_ty: Ty, + pub return_ty: Ty, + pub witness_types: Vec, + pub witness_lifetimes: Vec, +} + #[derive(Clone, PartialEq, Eq, Debug)] pub struct AdtFlags { pub upstream: bool, diff --git a/chalk-parse/src/parser.lalrpop b/chalk-parse/src/parser.lalrpop index 7947774c75c..4317bc7615d 100644 --- a/chalk-parse/src/parser.lalrpop +++ b/chalk-parse/src/parser.lalrpop @@ -18,6 +18,7 @@ Item: Option = { ClosureDefn => Some(Item::ClosureDefn(<>)), TraitDefn => Some(Item::TraitDefn(<>)), OpaqueTyDefn => Some(Item::OpaqueTyDefn(<>)), + GeneratorDefn => Some(Item::GeneratorDefn(<>)), Impl => Some(Item::Impl(<>)), Clause => Some(Item::Clause(<>)), ForeignType => Some(Item::Foreign(<>)), @@ -163,6 +164,23 @@ FnDefn: FnDefn = { } }; +GeneratorDefn: GeneratorDefn = { + "generator" > "[" "resume" "=" "," "yield" "=" "]" + "{" + "upvars" "[" > "]" + "witnesses" "[" > "]" + "}" => GeneratorDefn { + name: n, + variable_kinds: p, + upvars: upvars, + witness_lifetimes: l.unwrap_or_default(), + resume_ty: resume, + yield_ty: yield_ty, + return_ty: ret_ty.unwrap_or_else(|| Ty::Tuple { types: Vec::new() }), + witness_types: witnesses + } +} + FnAbi: FnAbi = "extern" "\"" "\"" => FnAbi(id.str); FnArg: FnArg = { diff --git a/chalk-solve/src/clauses.rs b/chalk-solve/src/clauses.rs index ebea5efaf1f..f0f1cbd9635 100644 --- a/chalk-solve/src/clauses.rs +++ b/chalk-solve/src/clauses.rs @@ -1,6 +1,7 @@ use self::builder::ClauseBuilder; use self::env_elaborator::elaborate_env_clauses; use self::program_clauses::ToProgramClauses; +use crate::goal_builder::GoalBuilder; use crate::split::Split; use crate::RustIrDatabase; use chalk_ir::cast::{Cast, Caster}; @@ -53,7 +54,27 @@ fn constituent_types( .cloned() .collect(), + TypeName::Generator(generator_id) => { + let generator_datum = &db.generator_datum(generator_id); + let generator_datum_bound = generator_datum + .input_output + .substitute(interner, &app_ty.substitution); + + let mut tys = generator_datum_bound.upvars; + tys.push( + ApplicationTy { + name: TypeName::GeneratorWitness(generator_id), + substitution: app_ty.substitution.clone(), + } + .intern(interner), + ); + tys + } + TypeName::Closure(_) => panic!("this function should not be called for closures"), + TypeName::GeneratorWitness(_) => { + panic!("this function should not be called for generator witnesses") + } TypeName::Foreign(_) => panic!("constituent_types of foreign types are unknown!"), TypeName::Error => Vec::new(), TypeName::OpaqueType(_) => unimplemented!(), @@ -142,6 +163,10 @@ pub fn push_auto_trait_impls( }); } + TypeName::GeneratorWitness(generator_id) => { + push_auto_trait_impls_generator_witness(builder, auto_trait_id, generator_id); + } + // app_ty implements AutoTrait if all constituents of app_ty implement AutoTrait _ => { let conditions = constituent_types(builder.db, app_ty) @@ -212,6 +237,89 @@ pub fn push_auto_trait_impls_opaque( }); } +#[instrument(level = "debug", skip(builder))] +pub fn push_auto_trait_impls_generator_witness( + builder: &mut ClauseBuilder<'_, I>, + auto_trait_id: TraitId, + generator_id: GeneratorId, +) { + let witness_datum = builder.db.generator_witness_datum(generator_id); + let interner = builder.interner(); + + // Must be an auto trait. + assert!(builder.db.trait_datum(auto_trait_id).is_auto_trait()); + + // Auto traits never have generic parameters of their own (apart from `Self`). + assert_eq!( + builder.db.trait_datum(auto_trait_id).binders.len(interner), + 1 + ); + + // Push binders for the generator generic parameters. These can be used by + // both upvars and witness types + builder.push_binders(&witness_datum.inner_types, |builder, inner_types| { + let witness_ty = ApplicationTy { + name: TypeName::GeneratorWitness(generator_id), + substitution: builder.substitution_in_scope(), + } + .intern(interner); + + // trait_ref = `GeneratorWitness<...>: MyAutoTrait` + let auto_trait_ref = TraitRef { + trait_id: auto_trait_id, + substitution: Substitution::from1(interner, witness_ty), + }; + + // Create a goal of the form: + // forall { + // WitnessType1: MyAutoTrait, + // ... + // WitnessTypeN: MyAutoTrait, + // + // } + // + // where `L0, L1, ...LN` are our existentially bound witness lifetimes, + // and `P0, P1, ..., PN` are the normal generator generics. + // + // We create a 'forall' goal due to the fact that our witness lifetimes + // are *existentially* quantified - the precise reigon is erased during + // type checking, so we just know that the type takes *some* region + // as a parameter. Therefore, we require that the auto trait bound + // hold for *all* regions, which guarantees that the bound will + // hold for the original lifetime (before it was erased). + // + // This does not take into account well-formed information from + // the witness types. For example, if we have the type + // `struct Foo<'a, 'b> { val: &'a &'b u8 }` + // then `'b: 'a` must hold for `Foo<'a, 'b>` to be well-formed. + // If we have `Foo<'a, 'b>` stored as a witness type, we will + // not currently use this information to determine a more precise + // relationship between 'a and 'b. In the future, we will likely + // do this to avoid incorrectly rejecting correct code. + let gb = &mut GoalBuilder::new(builder.db); + let witness_goal = gb.forall( + &inner_types.types, + auto_trait_id, + |gb, _subst, types, auto_trait_id| { + Goal::new( + gb.interner(), + GoalData::All(Goals::from_iter( + gb.interner(), + types.iter().map(|witness_ty| TraitRef { + trait_id: auto_trait_id, + substitution: Substitution::from1(gb.interner(), witness_ty.clone()), + }), + )), + ) + }, + ); + + // GeneratorWitnessType: AutoTrait :- forall<...> ... + // where 'forall<...> ...' is the goal described above. + builder.push_clause(auto_trait_ref, std::iter::once(witness_goal)); + }) +} + /// Given some goal `goal` that must be proven, along with /// its `environment`, figures out the program clauses that apply /// to this goal from the Rust program. So for example if the goal @@ -689,7 +797,9 @@ fn match_type_name( | TypeName::Array | TypeName::Never | TypeName::Closure(_) - | TypeName::Foreign(_) => { + | TypeName::Foreign(_) + | TypeName::Generator(_) + | TypeName::GeneratorWitness(_) => { builder.push_fact(WellFormed::Ty(application.clone().intern(interner))) } } diff --git a/chalk-solve/src/clauses/builtin_traits/copy.rs b/chalk-solve/src/clauses/builtin_traits/copy.rs index d8528da8f2e..010a95d8762 100644 --- a/chalk-solve/src/clauses/builtin_traits/copy.rs +++ b/chalk-solve/src/clauses/builtin_traits/copy.rs @@ -76,6 +76,8 @@ pub fn add_copy_program_clauses( | TypeName::Slice | TypeName::OpaqueType(_) | TypeName::Foreign(_) + | TypeName::Generator(_) + | TypeName::GeneratorWitness(_) | TypeName::Error => {} }, diff --git a/chalk-solve/src/clauses/builtin_traits/sized.rs b/chalk-solve/src/clauses/builtin_traits/sized.rs index b256a4bf828..437d669ee6e 100644 --- a/chalk-solve/src/clauses/builtin_traits/sized.rs +++ b/chalk-solve/src/clauses/builtin_traits/sized.rs @@ -87,6 +87,8 @@ pub fn add_sized_program_clauses( | TypeName::FnDef(_) | TypeName::Scalar(_) | TypeName::Raw(_) + | TypeName::Generator(_) + | TypeName::GeneratorWitness(_) | TypeName::Ref(_) => builder.push_fact(trait_ref.clone()), TypeName::AssociatedType(_) diff --git a/chalk-solve/src/display.rs b/chalk-solve/src/display.rs index 677332ace30..9faf9baa37f 100644 --- a/chalk-solve/src/display.rs +++ b/chalk-solve/src/display.rs @@ -92,6 +92,11 @@ where let v = ws.db().fn_def_datum(id); write_item(f, &InternalWriterState::new(ws), &*v)?; } + RecordedItemId::Generator(id) => { + let generator = ws.db().generator_datum(id); + let witness = ws.db().generator_witness_datum(id); + write_item(f, &InternalWriterState::new(ws), &(&*generator, &*witness))?; + } } } Ok(()) diff --git a/chalk-solve/src/display/items.rs b/chalk-solve/src/display/items.rs index 62a2154d651..463c1ea4864 100644 --- a/chalk-solve/src/display/items.rs +++ b/chalk-solve/src/display/items.rs @@ -63,6 +63,12 @@ macro_rules! write_flags { }; } +impl<'a, I: Interner> RenderAsRust for (&'a GeneratorDatum, &'a GeneratorWitnessDatum) { + fn fmt(&self, _s: &InternalWriterState<'_, I>, _f: &'_ mut Formatter<'_>) -> Result { + unimplemented!() + } +} + impl RenderAsRust for AdtDatum { fn fmt(&self, s: &InternalWriterState<'_, I>, f: &'_ mut Formatter<'_>) -> Result { // When support for Self in structs is added, self_binding should be diff --git a/chalk-solve/src/display/stub.rs b/chalk-solve/src/display/stub.rs index 2ad5791d499..db1d1453162 100644 --- a/chalk-solve/src/display/stub.rs +++ b/chalk-solve/src/display/stub.rs @@ -2,6 +2,7 @@ //! queried. use std::sync::Arc; +use crate::rust_ir::{GeneratorDatum, GeneratorWitnessDatum}; use crate::{ rust_ir::{ AdtDatumBound, AdtKind, AdtVariantDatum, AssociatedTyDatumBound, FnDefDatumBound, @@ -10,7 +11,8 @@ use crate::{ RustIrDatabase, }; use chalk_ir::{ - interner::Interner, ApplicationTy, Binders, CanonicalVarKinds, TypeName, VariableKinds, + interner::Interner, ApplicationTy, Binders, CanonicalVarKinds, GeneratorId, TypeName, + VariableKinds, }; #[derive(Debug)] @@ -209,6 +211,17 @@ impl> RustIrDatabase for StubWrapper<'_, D unimplemented!("cannot stub closures") } + fn generator_datum(&self, _generator_id: GeneratorId) -> Arc> { + unimplemented!("cannot stub generator") + } + + fn generator_witness_datum( + &self, + _generator_id: GeneratorId, + ) -> Arc> { + unimplemented!("cannot stub generator witness") + } + fn closure_fn_substitution( &self, _closure_id: chalk_ir::ClosureId, diff --git a/chalk-solve/src/display/ty.rs b/chalk-solve/src/display/ty.rs index 8454cd15b67..e965a0213e3 100644 --- a/chalk-solve/src/display/ty.rs +++ b/chalk-solve/src/display/ty.rs @@ -218,6 +218,8 @@ impl RenderAsRust for ApplicationTy { TypeName::FnDef(_) => write!(f, "")?, TypeName::Closure(..) => write!(f, "")?, TypeName::Foreign(_) => write!(f, "")?, + TypeName::Generator(..) => write!(f, "")?, + TypeName::GeneratorWitness(..) => write!(f, "")?, TypeName::Array => write!( f, diff --git a/chalk-solve/src/lib.rs b/chalk-solve/src/lib.rs index 583595e11c7..f9dcb1ad0e9 100644 --- a/chalk-solve/src/lib.rs +++ b/chalk-solve/src/lib.rs @@ -55,6 +55,15 @@ pub trait RustIrDatabase: Debug { /// Returns the datum for the ADT with the given id. fn adt_datum(&self, adt_id: AdtId) -> Arc>; + /// Returns the generator datum for the generator with the given id. + fn generator_datum(&self, generator_id: GeneratorId) -> Arc>; + + /// Returns the generator witness datum for the generator with the given id. + fn generator_witness_datum( + &self, + generator_id: GeneratorId, + ) -> Arc>; + /// Returns the representation for the ADT definition with the given id. fn adt_repr(&self, id: AdtId) -> AdtRepr; diff --git a/chalk-solve/src/logging_db.rs b/chalk-solve/src/logging_db.rs index 3681e8c2807..5fc97d542f2 100644 --- a/chalk-solve/src/logging_db.rs +++ b/chalk-solve/src/logging_db.rs @@ -120,6 +120,19 @@ where self.ws.db().adt_datum(adt_id) } + fn generator_datum(&self, generator_id: GeneratorId) -> Arc> { + self.record(generator_id); + self.ws.db().borrow().generator_datum(generator_id) + } + + fn generator_witness_datum( + &self, + generator_id: GeneratorId, + ) -> Arc> { + self.record(generator_id); + self.ws.db().borrow().generator_witness_datum(generator_id) + } + fn adt_repr(&self, id: AdtId) -> AdtRepr { self.record(id); self.ws.db().adt_repr(id) @@ -345,6 +358,18 @@ where self.db.adt_datum(adt_id) } + fn generator_datum(&self, generator_id: GeneratorId) -> Arc> { + self.db.borrow().generator_datum(generator_id) + } + + /// Returns the generator witness datum for the generator with the given id. + fn generator_witness_datum( + &self, + generator_id: GeneratorId, + ) -> Arc> { + self.db.borrow().generator_witness_datum(generator_id) + } + fn adt_repr(&self, id: AdtId) -> AdtRepr { self.db.adt_repr(id) } @@ -464,6 +489,7 @@ pub enum RecordedItemId { Impl(ImplId), OpaqueTy(OpaqueTyId), FnDef(FnDefId), + Generator(GeneratorId), } impl From> for RecordedItemId { @@ -496,6 +522,12 @@ impl From> for RecordedItemId { } } +impl From> for RecordedItemId { + fn from(v: GeneratorId) -> Self { + RecordedItemId::Generator(v) + } +} + /// Utility for implementing Ord for RecordedItemId. #[derive(PartialEq, Eq, PartialOrd, Ord)] enum OrderedItemId<'a, DefId, AdtId> { @@ -512,6 +544,7 @@ impl RecordedItemId { RecordedItemId::Trait(TraitId(x)) | RecordedItemId::Impl(ImplId(x)) | RecordedItemId::OpaqueTy(OpaqueTyId(x)) + | RecordedItemId::Generator(GeneratorId(x)) | RecordedItemId::FnDef(FnDefId(x)) => OrderedItemId::DefId(x), RecordedItemId::Adt(AdtId(x)) => OrderedItemId::AdtId(x), } diff --git a/chalk-solve/src/logging_db/id_collector.rs b/chalk-solve/src/logging_db/id_collector.rs index 9c3dd8d866f..3750aa14ef0 100644 --- a/chalk-solve/src/logging_db/id_collector.rs +++ b/chalk-solve/src/logging_db/id_collector.rs @@ -53,6 +53,7 @@ pub fn collect_unrecorded_ids<'i, I: Interner, DB: RustIrDatabase>( .fn_def_datum(fn_def) .visit_with(&mut collector, DebruijnIndex::INNERMOST); } + RecordedItemId::Generator(_generator_id) => unimplemented!(), RecordedItemId::Trait(trait_id) => { let trait_datum = collector.db.trait_datum(trait_id); diff --git a/chalk-solve/src/rust_ir.rs b/chalk-solve/src/rust_ir.rs index 3dabc05f2fa..eb8588342e0 100644 --- a/chalk-solve/src/rust_ir.rs +++ b/chalk-solve/src/rust_ir.rs @@ -653,6 +653,68 @@ pub struct OpaqueTyDatumBound { pub where_clauses: Binders>>, } +/// Represents a generator type. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Fold, HasInterner)] +pub struct GeneratorDatum { + /// All of the nested types for this generator. The `Binder` + /// represents the types and lifetimes that this generator is generic over - + /// this behaves in the same way as `AdtDatun.binders` + pub input_output: Binders>, +} + +/// The nested types for a generator. This always appears inside a `GeneratorDatum` +#[derive(Clone, Debug, PartialEq, Eq, Hash, Fold, HasInterner)] +pub struct GeneratorInputOutputDatum { + /// The generator resume type - a value of this type + /// is supplied by the caller when resuming the generator. + /// Currently, this plays no rule in goal resolution. + pub resume_type: Ty, + /// The generator yield type - a value of this type + /// is supplied by the generator during a yield. + /// Currently, this plays no role in goal resolution. + pub yield_type: Ty, + /// The generator return type - a value of this type + /// is supplied by the generator when it returns. + /// Currently, this plays no role in goal resolution + pub return_type: Ty, + /// The upvars stored by the generator. These represent + /// types captured from the generator's environment, + /// and are stored across all yields. These types (along with the witness types) + /// are considered 'constituent types' for the purposes of determining auto trait + /// implementations - that its, a generator impls an auto trait A + /// iff all of its constituent types implement A. + pub upvars: Vec>, +} + +/// The generator witness data. Each `GeneratorId` has both a `GeneratorDatum` +/// and a `GeneratorWitnessDatum` - these represent two distinct types in Rust. +/// `GeneratorWitnessDatum` is logically 'inside' a generator - this only +/// matters when we treat the witness type as a 'constituent type for the +/// purposes of determining auto trait implementations. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Fold, HasInterner)] +pub struct GeneratorWitnessDatum { + /// This binder is identical to the `input_output` binder in `GeneratorWitness` - + /// it binds the types and lifetimes that the generator is generic over. + /// There is an additional binder inside `GeneratorWitnessExistential`, which + /// is treated specially. + pub inner_types: Binders>, +} + +/// The generator witness types, together with existentially bound lifetimes. +/// Each 'witness type' represents a type stored inside the generator across +/// a yield. When a generator type is constructed, the precise region relationships +/// found in the generator body are erased. As a result, we are left with existential +/// lifetimes - each type is parameterized over *some* lifetimes, but we do not +/// know their precise values. +/// +/// Unlike the binder in `GeneratorWitnessDatum`, this `Binder` never gets substituted +/// via an `ApplicationTy`. Instead, we handle this `Binders` specially when determining +/// auto trait impls. See `push_auto_trait_impls_generator_witness` for more details. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Fold, HasInterner)] +pub struct GeneratorWitnessExistential { + pub types: Binders>>, +} + #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] pub enum Polarity { Positive, diff --git a/tests/display/unique_names.rs b/tests/display/unique_names.rs index a645ffd18a0..86bdee976d4 100644 --- a/tests/display/unique_names.rs +++ b/tests/display/unique_names.rs @@ -96,6 +96,18 @@ where ) -> std::sync::Arc> { self.db.associated_ty_value(id) } + fn generator_datum( + &self, + generator_id: chalk_ir::GeneratorId, + ) -> std::sync::Arc> { + self.db.generator_datum(generator_id) + } + fn generator_witness_datum( + &self, + generator_id: chalk_ir::GeneratorId, + ) -> std::sync::Arc> { + self.db.generator_witness_datum(generator_id) + } fn opaque_ty_data( &self, id: chalk_ir::OpaqueTyId, diff --git a/tests/integration/panic.rs b/tests/integration/panic.rs index 70bb366f56f..4994b3933d5 100644 --- a/tests/integration/panic.rs +++ b/tests/integration/panic.rs @@ -154,6 +154,17 @@ impl RustIrDatabase for MockDatabase { unimplemented!() } + fn generator_datum(&self, generator_id: GeneratorId) -> Arc> { + unimplemented!() + } + + fn generator_witness_datum( + &self, + generator_id: GeneratorId, + ) -> Arc> { + unimplemented!() + } + // All `Bar` impls fn impls_for_trait( &self, diff --git a/tests/test/generators.rs b/tests/test/generators.rs new file mode 100644 index 00000000000..2b2835ef031 --- /dev/null +++ b/tests/test/generators.rs @@ -0,0 +1,89 @@ +use super::*; + +#[test] +fn generator_test() { + test! { + program { + #[auto] trait Send { } + + + struct StructOne {} + struct NotSend {} + struct SendSameLifetime<'a, 'b, T> { val: &'a T, other: &'b T } + impl<'a, T> Send for SendSameLifetime<'a, 'a, T> {} + + struct SendAnyLifetime<'a, 'b, T> { val: &'a u8, other: &'b u8, field: T } + + impl !Send for NotSend {} + struct StructThree<'a> { val: &'a () } + + generator empty_gen<>[resume = (), yield = ()] { + upvars [] + witnesses [] + } + + generator upvar_lifetime_restrict[resume = (), yield = ()] { + upvars [T; StructOne] + witnesses for<'a, 'b> [SendSameLifetime<'a, 'b, T>] + } + + generator send_any_lifetime[resume = (), yield = ()] { + upvars [] + witnesses for<'a, 'b> [SendAnyLifetime<'a, 'b, T>; u8] + } + + + generator not_send_resume_yield<>[resume = NotSend, yield = NotSend] { + upvars [] + witnesses [] + } + + } + + goal { + WellFormed(empty_gen) + } yields { + "Unique" + } + + goal { + empty_gen: Send + } yields { + "Unique" + } + + goal { + forall { + upvar_lifetime_restrict: Send + } + } yields { + "No possible solution" + } + + goal { + forall { + if (T: Send) { + upvar_lifetime_restrict: Send + } + } + } yields { + "Unique; substitution [], lifetime constraints [InEnvironment { environment: Env([for<> FromEnv(!1_0: Send)]), goal: '!2_0: '!2_1 }, InEnvironment { environment: Env([for<> FromEnv(!1_0: Send)]), goal: '!2_1: '!2_0 }]" + } + + goal { + not_send_resume_yield: Send + } yields { + "Unique; substitution [], lifetime constraints []" + } + + goal { + forall { + if (T: Send) { + send_any_lifetime: Send + } + } + } yields { + "Unique; substitution [], lifetime constraints []" + } + } +} diff --git a/tests/test/mod.rs b/tests/test/mod.rs index 94485ae57b8..e8bcdb55c6d 100644 --- a/tests/test/mod.rs +++ b/tests/test/mod.rs @@ -331,6 +331,7 @@ mod cycle; mod existential_types; mod fn_def; mod foreign_types; +mod generators; mod implied_bounds; mod impls; mod misc; From 905bb9483b4c7f00b6d66a762160fdfe57b91448 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Fri, 25 Sep 2020 19:55:33 -0400 Subject: [PATCH 2/4] Start working on some docs --- book/src/types/rust_types.md | 18 --------- book/src/types/rust_types/application_ty.md | 42 +++++++++++++++++++++ 2 files changed, 42 insertions(+), 18 deletions(-) diff --git a/book/src/types/rust_types.md b/book/src/types/rust_types.md index 97b3d221372..aed9e28177f 100644 --- a/book/src/types/rust_types.md +++ b/book/src/types/rust_types.md @@ -132,24 +132,6 @@ because of the above subtyping rules there are actually a range of values that `V` could have and still be equal with `F`. This may or may not be something to consider revisiting. -### Generator witness types - -The `GeneratorWitness` variant wraps a `GeneratorWitness` type. These -witnesses represent the types that may be part of a generator -state. Unlike other types, witnesses include bound, existential -lifetimes, which refer to lifetimes within the suspended stack frame. -You can think of it as a type like `exists<'a> { (T...) }`. - -Witnesses are very similar to an `Apply` type, but it has a binder for -the erased lifetime(s), which must be handled specifically in equating -and so forth. In many ways, witnesses are also quite similar to `Fn` -types, and it is not out of the question that these two could be -unified; however, they are quite distinct semantically and so that -would be an annoying mismatch in other parts of the system. -Witnesses are also similar to a `Dyn` type, in that they represent an -existential type, but in contrast to `Dyn`, what we know here is -not a *predicate* but rather some upper bound on the set of types -contained within. ### Alias types diff --git a/book/src/types/rust_types/application_ty.md b/book/src/types/rust_types/application_ty.md index 7027893ce68..c964ad6710e 100644 --- a/book/src/types/rust_types/application_ty.md +++ b/book/src/types/rust_types/application_ty.md @@ -22,5 +22,47 @@ _)`) or "fixed-length array" `[_; _]`. Note that the precise set of these built-in types is defined by the `Interner` and is unknown to chalk-ir. +## [`TypeName`] variants + +### Generator + +A `Generator` represents a Rust generator. There are three major components +to a generator: + +* Upvars - similar to closure upvars, they reference values outside of the generator, + and are stored across al yield points. +* Resume/yield/return types - the types produced/consumed by various generator methods. + These are not stored in the generator across yield points - they are only + used when the generator is running. +* Generator witness - see the `Generator Witness` section below. + +Of these types, only upvars and resume/yield/return are stored directly in +`TypeName::Generator`. The generator witness is implicitly associated with the generator +by virtue of sharing the same `GeneratorId`. It is only used when determining auto trait +impls, where it is considered a 'constituent type'. + +### Generator witness types + +The `GeneratorWitness` variant represents the generator witness of +the generator with id `GeneratorId`. + +The generator witness contains multiple witness types, +which represent the types that may be part of a generator +state. Unlike other types, witnesses include bound, existential +lifetimes, which refer to lifetimes within the suspended stack frame. +You can think of it as a type like `exists<'a> { (T...) }`. + +Witnesses are very similar to an `Apply` type, but it has a binder for +the erased lifetime(s), which must be handled specifically in equating +and so forth. In many ways, witnesses are also quite similar to `Fn` +types, and it is not out of the question that these two could be +unified; however, they are quite distinct semantically and so that +would be an annoying mismatch in other parts of the system. +Witnesses are also similar to a `Dyn` type, in that they represent an +existential type, but in contrast to `Dyn`, what we know here is +not a *predicate* but rather some upper bound on the set of types +contained within. + + [`TypeName`]: http://rust-lang.github.io/chalk/chalk_ir/enum.TypeName.html [`Substitution`]: http://rust-lang.github.io/chalk/chalk_ir/struct.Substitution.html From b982f8000322aa06ab0b848f2475a8f14e17e9d9 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Tue, 29 Sep 2020 18:41:19 -0400 Subject: [PATCH 3/4] Further explain witness types in book --- book/src/types/rust_types/application_ty.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/book/src/types/rust_types/application_ty.md b/book/src/types/rust_types/application_ty.md index c964ad6710e..062013167c4 100644 --- a/book/src/types/rust_types/application_ty.md +++ b/book/src/types/rust_types/application_ty.md @@ -48,7 +48,10 @@ the generator with id `GeneratorId`. The generator witness contains multiple witness types, which represent the types that may be part of a generator -state. Unlike other types, witnesses include bound, existential +state - that is, the types of all variables that may be live across +a `yield` point. + +Unlike other types, witnesses include bound, existential lifetimes, which refer to lifetimes within the suspended stack frame. You can think of it as a type like `exists<'a> { (T...) }`. From e7a089294eb5b71e19f4e149afd8135986e3f5c4 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Tue, 29 Sep 2020 18:41:45 -0400 Subject: [PATCH 4/4] Define generator witness syntax to use `exists<>` instead of `for<>` --- chalk-parse/src/parser.lalrpop | 3 ++- tests/test/generators.rs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/chalk-parse/src/parser.lalrpop b/chalk-parse/src/parser.lalrpop index 4317bc7615d..3aca74a0a2d 100644 --- a/chalk-parse/src/parser.lalrpop +++ b/chalk-parse/src/parser.lalrpop @@ -168,7 +168,7 @@ GeneratorDefn: GeneratorDefn = { "generator" > "[" "resume" "=" "," "yield" "=" "]" "{" "upvars" "[" > "]" - "witnesses" "[" > "]" + "witnesses" "[" > "]" "}" => GeneratorDefn { name: n, variable_kinds: p, @@ -391,6 +391,7 @@ TyWithoutId: Ty = { "[" ";" "]" => Ty::Array { ty: Box::new(t), len }, }; +ExistsLifetimes: Vec = "exists" "<" > ">" => <>; ForLifetimes: Vec = "for" "<" > ">" => <>; ScalarType: ScalarType = { diff --git a/tests/test/generators.rs b/tests/test/generators.rs index 2b2835ef031..4c3b428848f 100644 --- a/tests/test/generators.rs +++ b/tests/test/generators.rs @@ -24,12 +24,12 @@ fn generator_test() { generator upvar_lifetime_restrict[resume = (), yield = ()] { upvars [T; StructOne] - witnesses for<'a, 'b> [SendSameLifetime<'a, 'b, T>] + witnesses exists<'a, 'b> [SendSameLifetime<'a, 'b, T>] } generator send_any_lifetime[resume = (), yield = ()] { upvars [] - witnesses for<'a, 'b> [SendAnyLifetime<'a, 'b, T>; u8] + witnesses exists<'a, 'b> [SendAnyLifetime<'a, 'b, T>; u8] }