From a9afe421e175a7e3f10418490c23741038878b38 Mon Sep 17 00:00:00 2001 From: Mikhail Babenko Date: Mon, 6 Apr 2020 10:49:28 +0300 Subject: [PATCH 1/4] add Sized well-known impl and wf check --- chalk-integration/src/db.rs | 5 ++ chalk-integration/src/lowering.rs | 26 ++++++-- chalk-integration/src/program.rs | 14 ++++- chalk-integration/src/query.rs | 1 - chalk-rust-ir/src/lib.rs | 8 ++- chalk-solve/src/clauses/builtin_traits.rs | 42 ++++++++++++- chalk-solve/src/lib.rs | 3 + chalk-solve/src/wf.rs | 34 +++++++++- tests/test/auto_traits.rs | 3 +- tests/test/wf_lowering.rs | 76 +++++++++++++++++++++++ 10 files changed, 197 insertions(+), 15 deletions(-) diff --git a/chalk-integration/src/db.rs b/chalk-integration/src/db.rs index 515fd90dd5a..15cabdb80be 100644 --- a/chalk-integration/src/db.rs +++ b/chalk-integration/src/db.rs @@ -21,6 +21,7 @@ use chalk_rust_ir::AssociatedTyDatum; use chalk_rust_ir::AssociatedTyValue; use chalk_rust_ir::AssociatedTyValueId; use chalk_rust_ir::ImplDatum; +use chalk_rust_ir::LangItem; use chalk_rust_ir::StructDatum; use chalk_rust_ir::TraitDatum; use chalk_solve::RustIrDatabase; @@ -137,6 +138,10 @@ impl RustIrDatabase for ChalkDatabase { .impl_provided_for(auto_trait_id, struct_id) } + fn require_lang_item(&self, lang_item: LangItem) -> TraitId { + self.program_ir().unwrap().require_lang_item(lang_item) + } + fn interner(&self) -> &ChalkIr { &ChalkIr } diff --git a/chalk-integration/src/lowering.rs b/chalk-integration/src/lowering.rs index c2f156a6eaa..64f828cdea7 100644 --- a/chalk-integration/src/lowering.rs +++ b/chalk-integration/src/lowering.rs @@ -3,7 +3,7 @@ use chalk_ir::interner::ChalkIr; use chalk_ir::{self, AssocTypeId, BoundVar, DebruijnIndex, ImplId, StructId, TraitId}; use chalk_parse::ast::*; use chalk_rust_ir as rust_ir; -use chalk_rust_ir::{Anonymize, AssociatedTyValueId, IntoWhereClauses, ToParameter}; +use chalk_rust_ir::{Anonymize, AssociatedTyValueId, IntoWhereClauses, LangItem, ToParameter}; use lalrpop_intern::intern; use std::collections::BTreeMap; use std::sync::Arc; @@ -246,6 +246,7 @@ impl LowerProgram for Program { let mut struct_data = BTreeMap::new(); let mut trait_data = BTreeMap::new(); + let mut trait_lang_items = BTreeMap::new(); let mut impl_data = BTreeMap::new(); let mut associated_ty_data = BTreeMap::new(); let mut associated_ty_values = BTreeMap::new(); @@ -267,10 +268,24 @@ impl LowerProgram for Program { } Item::TraitDefn(ref trait_defn) => { let trait_id = TraitId(raw_id); - trait_data.insert( - trait_id, - Arc::new(trait_defn.lower_trait(trait_id, &empty_env)?), - ); + let trait_datum = trait_defn.lower_trait(trait_id, &empty_env)?; + + if let Some(well_known) = trait_datum.well_known { + if let Some(lang_item) = match well_known { + rust_ir::WellKnownTrait::SizedTrait => Some(LangItem::SizedTrait), + _ => None, + } { + use std::collections::btree_map::Entry; + match trait_lang_items.entry(lang_item) { + Entry::Vacant(vacant) => vacant.insert(trait_id), + Entry::Occupied(_) => { + return Err(RustIrError::DuplicateLangItem(lang_item)) + } + }; + } + } + + trait_data.insert(trait_id, Arc::new(trait_datum)); for assoc_ty_defn in &trait_defn.assoc_ty_defns { let lookup = &associated_ty_lookups[&(trait_id, assoc_ty_defn.name.str)]; @@ -367,6 +382,7 @@ impl LowerProgram for Program { trait_kinds, struct_data, trait_data, + trait_lang_items, impl_data, associated_ty_values, associated_ty_data, diff --git a/chalk-integration/src/program.rs b/chalk-integration/src/program.rs index e1ef452c637..229b0a360c8 100644 --- a/chalk-integration/src/program.rs +++ b/chalk-integration/src/program.rs @@ -9,8 +9,8 @@ use chalk_ir::{ TraitId, Ty, TyData, TypeName, }; use chalk_rust_ir::{ - AssociatedTyDatum, AssociatedTyValue, AssociatedTyValueId, ImplDatum, ImplType, StructDatum, - TraitDatum, + AssociatedTyDatum, AssociatedTyValue, AssociatedTyValueId, ImplDatum, ImplType, LangItem, + StructDatum, TraitDatum, }; use chalk_solve::split::Split; use chalk_solve::RustIrDatabase; @@ -45,6 +45,9 @@ pub struct Program { /// For each trait: pub trait_data: BTreeMap, Arc>>, + /// For each trait lang item + pub trait_lang_items: BTreeMap>, + /// For each associated ty declaration `type Foo` found in a trait: pub associated_ty_data: BTreeMap, Arc>>, @@ -309,6 +312,13 @@ impl RustIrDatabase for Program { }) } + fn require_lang_item(&self, lang_item: LangItem) -> TraitId { + *self + .trait_lang_items + .get(&lang_item) + .unwrap_or_else(|| panic!("No lang item found for {:?}", lang_item)) + } + fn interner(&self) -> &ChalkIr { &ChalkIr } diff --git a/chalk-integration/src/query.rs b/chalk-integration/src/query.rs index c1d0c8d0202..c71a59f4d21 100644 --- a/chalk-integration/src/query.rs +++ b/chalk-integration/src/query.rs @@ -100,7 +100,6 @@ fn checked_program(db: &impl LoweringDatabase) -> Result, ChalkErro let () = tls::set_current_program(&program, || -> Result<(), ChalkError> { let solver = wf::WfSolver::new(db, db.solver_choice()); - for &id in program.struct_data.keys() { solver.verify_struct_decl(id)?; } diff --git a/chalk-rust-ir/src/lib.rs b/chalk-rust-ir/src/lib.rs index e9b9bd42c0b..9f33d387132 100644 --- a/chalk-rust-ir/src/lib.rs +++ b/chalk-rust-ir/src/lib.rs @@ -15,8 +15,10 @@ use chalk_ir::{ }; use std::iter; -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum LangItem {} +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum LangItem { + SizedTrait, +} /// Identifier for an "associated type value" found in some impl. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -78,7 +80,7 @@ impl StructDatum { } } -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, Fold, HasInterner)] pub struct StructDatumBound { pub fields: Vec>, pub where_clauses: Vec>, diff --git a/chalk-solve/src/clauses/builtin_traits.rs b/chalk-solve/src/clauses/builtin_traits.rs index d7793eee6a8..2b7a3667e03 100644 --- a/chalk-solve/src/clauses/builtin_traits.rs +++ b/chalk-solve/src/clauses/builtin_traits.rs @@ -1,6 +1,8 @@ +use std::iter; + use super::builder::ClauseBuilder; use crate::{Interner, RustIrDatabase, TraitRef, WellKnownTrait}; -use chalk_ir::TyData; +use chalk_ir::{ApplicationTy, Substitution, TyData, TypeName}; /// For well known traits we have special hard-coded impls, either as an /// optimization or to enforce special rules for correctness. @@ -16,8 +18,44 @@ pub fn add_builtin_program_clauses( } match well_known { - WellKnownTrait::SizedTrait => { /* TODO */ } + WellKnownTrait::SizedTrait => add_sized_program_clauses(db, builder, trait_ref, ty), WellKnownTrait::CopyTrait => { /* TODO */ } WellKnownTrait::CloneTrait => { /* TODO */ } } } + +fn add_sized_program_clauses( + db: &dyn RustIrDatabase, + builder: &mut ClauseBuilder<'_, I>, + trait_ref: &TraitRef, + ty: &TyData, +) { + let interner = db.interner(); + + let (struct_id, substitution) = match ty { + TyData::Apply(ApplicationTy { + name: TypeName::Struct(struct_id), + substitution, + }) => (*struct_id, substitution), + _ => return, + }; + + let struct_datum = db.struct_datum(struct_id); + + if struct_datum.binders.map_ref(|b| b.fields.is_empty()).value { + builder.push_fact(trait_ref.clone()); + return; + } + + let last_field_ty = struct_datum + .binders + .map_ref(|b| b.fields.last().unwrap()) + .substitute(interner, substitution) + .clone(); + + let last_field_sized_goal = TraitRef { + trait_id: trait_ref.trait_id, + substitution: Substitution::from1(interner, last_field_ty), + }; + builder.push_clause(trait_ref.clone(), iter::once(last_field_sized_goal)); +} diff --git a/chalk-solve/src/lib.rs b/chalk-solve/src/lib.rs index 0ad91893622..68a71019541 100644 --- a/chalk-solve/src/lib.rs +++ b/chalk-solve/src/lib.rs @@ -76,6 +76,9 @@ pub trait RustIrDatabase: Debug { false } + /// Returns id of a trait lang item, if found + fn require_lang_item(&self, lang_item: LangItem) -> TraitId; + fn interner(&self) -> &I; } diff --git a/chalk-solve/src/wf.rs b/chalk-solve/src/wf.rs index c3a5f62337b..2124524b9a4 100644 --- a/chalk-solve/src/wf.rs +++ b/chalk-solve/src/wf.rs @@ -190,6 +190,8 @@ where let wg_goal = gb.forall(&struct_data, (), |gb, _, (fields, where_clauses), ()| { let interner = gb.interner(); + let sized_constraint_goal = compute_struct_sized_constraint(gb.db(), fields); + // (FromEnv(T: Eq) => ...) gb.implies( where_clauses @@ -203,7 +205,13 @@ where fields.fold(gb.interner(), &mut input_types); // ...in a where clause. where_clauses.fold(gb.interner(), &mut input_types); - gb.all(input_types.into_iter().map(|ty| ty.well_formed())) + + gb.all( + input_types + .into_iter() + .map(|ty| ty.well_formed().cast(interner)) + .chain(sized_constraint_goal.into_iter()), + ) }, ) }); @@ -464,3 +472,27 @@ fn compute_assoc_ty_goal( }, )) } + +fn compute_struct_sized_constraint( + db: &dyn RustIrDatabase, + fields: &[Ty], +) -> Option> { + if fields.len() <= 1 { + return None; + } + + let interner = db.interner(); + + let sized_trait = db.require_lang_item(LangItem::SizedTrait); + + Some(Goal::all( + interner, + fields[..fields.len() - 1].iter().map(|ty| { + TraitRef { + trait_id: sized_trait, + substitution: Substitution::from1(interner, ty.clone()), + } + .cast(interner) + }), + )) +} diff --git a/tests/test/auto_traits.rs b/tests/test/auto_traits.rs index 02d7313991f..d410dbb7f37 100644 --- a/tests/test/auto_traits.rs +++ b/tests/test/auto_traits.rs @@ -6,6 +6,7 @@ use super::*; fn auto_semantics() { test! { program { + #[lang(sized)] trait Sized { } #[auto] trait Send { } struct i32 { } @@ -13,7 +14,7 @@ fn auto_semantics() { struct Ptr { } impl Send for Ptr where T: Send { } - struct List { + struct List where T: Sized { data: T, next: Ptr> } diff --git a/tests/test/wf_lowering.rs b/tests/test/wf_lowering.rs index 482e50fda4b..feadd59b2bc 100644 --- a/tests/test/wf_lowering.rs +++ b/tests/test/wf_lowering.rs @@ -636,3 +636,79 @@ fn assoc_type_recursive_bound() { } } } + +#[test] +fn duplicate_lang_item() { + lowering_error! { + program { + #[lang(sized)] + trait Sized { } + + #[lang(sized)] + trait Sized2 { } + } error_msg { + "duplicate lang item `SizedTrait`" + } + } +} + +#[test] +fn struct_sized_constraints() { + lowering_error! { + program { + #[lang(sized)] + trait Sized { } + + struct S { + t1: T, + t2: T + } + } error_msg { + "type declaration `S` does not meet well-formedness requirements" + } + } + + lowering_success! { + program { + #[lang(sized)] + trait Sized { } + + struct Foo { } + + struct S { + t1: Foo, + t2: T + } + } + } + + lowering_success! { + program { + #[lang(sized)] + trait Sized { } + + struct S where T: Sized { + t1: T, + t2: T + } + } + } + + lowering_success! { + program { + #[lang(sized)] + trait Sized { } + + struct Foo {} + + struct G { + foo: S>, + s: S>> + } + + struct S { + t1: T + } + } + } +} From 583984195c75086b1191412366404842f93be767 Mon Sep 17 00:00:00 2001 From: Mikhail Babenko Date: Mon, 6 Apr 2020 11:15:59 +0300 Subject: [PATCH 2/4] forbid manually implementing Sized --- chalk-solve/src/wf.rs | 10 ++++++++-- tests/test/wf_lowering.rs | 13 +++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/chalk-solve/src/wf.rs b/chalk-solve/src/wf.rs index 2124524b9a4..10baa4ea588 100644 --- a/chalk-solve/src/wf.rs +++ b/chalk-solve/src/wf.rs @@ -232,7 +232,14 @@ where pub fn verify_trait_impl(&self, impl_id: ImplId) -> Result<(), WfError> { let interner = self.db.interner(); + let impl_datum = self.db.impl_datum(impl_id); + let trait_id = impl_datum.trait_id(); + + // You can't manually implement Sized + if let Some(WellKnownTrait::SizedTrait) = self.db.trait_datum(trait_id).well_known { + return Err(WfError::IllFormedTraitImpl(trait_id)); + } let impl_goal = Goal::all( interner, @@ -258,8 +265,7 @@ where if is_legal { Ok(()) } else { - let trait_ref = &impl_datum.binders.value.trait_ref; - Err(WfError::IllFormedTraitImpl(trait_ref.trait_id)) + Err(WfError::IllFormedTraitImpl(trait_id)) } } } diff --git a/tests/test/wf_lowering.rs b/tests/test/wf_lowering.rs index feadd59b2bc..fb53f87055c 100644 --- a/tests/test/wf_lowering.rs +++ b/tests/test/wf_lowering.rs @@ -711,4 +711,17 @@ fn struct_sized_constraints() { } } } + + lowering_error! { + program { + #[lang(sized)] + trait Sized { } + + struct Foo {} + + impl Sized for Foo {} + } error_msg { + "trait impl for `Sized` does not meet well-formedness requirements" + } + } } From ae8d536d97629e1ba34c2c901302aea72862d1f2 Mon Sep 17 00:00:00 2001 From: Mikhail Babenko Date: Tue, 7 Apr 2020 07:31:41 +0300 Subject: [PATCH 3/4] address nits --- chalk-solve/src/clauses/builtin_traits.rs | 44 ++---------------- .../src/clauses/builtin_traits/sized.rs | 46 +++++++++++++++++++ chalk-solve/src/wf.rs | 4 ++ 3 files changed, 54 insertions(+), 40 deletions(-) create mode 100644 chalk-solve/src/clauses/builtin_traits/sized.rs diff --git a/chalk-solve/src/clauses/builtin_traits.rs b/chalk-solve/src/clauses/builtin_traits.rs index 2b7a3667e03..928296b4025 100644 --- a/chalk-solve/src/clauses/builtin_traits.rs +++ b/chalk-solve/src/clauses/builtin_traits.rs @@ -1,8 +1,8 @@ -use std::iter; - use super::builder::ClauseBuilder; use crate::{Interner, RustIrDatabase, TraitRef, WellKnownTrait}; -use chalk_ir::{ApplicationTy, Substitution, TyData, TypeName}; +use chalk_ir::TyData; + +mod sized; /// For well known traits we have special hard-coded impls, either as an /// optimization or to enforce special rules for correctness. @@ -18,44 +18,8 @@ pub fn add_builtin_program_clauses( } match well_known { - WellKnownTrait::SizedTrait => add_sized_program_clauses(db, builder, trait_ref, ty), + WellKnownTrait::SizedTrait => sized::add_sized_program_clauses(db, builder, trait_ref, ty), WellKnownTrait::CopyTrait => { /* TODO */ } WellKnownTrait::CloneTrait => { /* TODO */ } } } - -fn add_sized_program_clauses( - db: &dyn RustIrDatabase, - builder: &mut ClauseBuilder<'_, I>, - trait_ref: &TraitRef, - ty: &TyData, -) { - let interner = db.interner(); - - let (struct_id, substitution) = match ty { - TyData::Apply(ApplicationTy { - name: TypeName::Struct(struct_id), - substitution, - }) => (*struct_id, substitution), - _ => return, - }; - - let struct_datum = db.struct_datum(struct_id); - - if struct_datum.binders.map_ref(|b| b.fields.is_empty()).value { - builder.push_fact(trait_ref.clone()); - return; - } - - let last_field_ty = struct_datum - .binders - .map_ref(|b| b.fields.last().unwrap()) - .substitute(interner, substitution) - .clone(); - - let last_field_sized_goal = TraitRef { - trait_id: trait_ref.trait_id, - substitution: Substitution::from1(interner, last_field_ty), - }; - builder.push_clause(trait_ref.clone(), iter::once(last_field_sized_goal)); -} diff --git a/chalk-solve/src/clauses/builtin_traits/sized.rs b/chalk-solve/src/clauses/builtin_traits/sized.rs new file mode 100644 index 00000000000..bc7d37a441d --- /dev/null +++ b/chalk-solve/src/clauses/builtin_traits/sized.rs @@ -0,0 +1,46 @@ +use std::iter; + +use crate::clauses::ClauseBuilder; +use crate::{Interner, RustIrDatabase, TraitRef}; +use chalk_ir::{ApplicationTy, Substitution, TyData, TypeName}; + +pub fn add_sized_program_clauses( + db: &dyn RustIrDatabase, + builder: &mut ClauseBuilder<'_, I>, + trait_ref: &TraitRef, + ty: &TyData, +) { + let interner = db.interner(); + + let (struct_id, substitution) = match ty { + TyData::Apply(ApplicationTy { + name: TypeName::Struct(struct_id), + substitution, + }) => (*struct_id, substitution), + // TODO(areredify) + // when #368 lands, extend this to handle everything accordingly + _ => return, + }; + + let struct_datum = db.struct_datum(struct_id); + + // Structs with no fields are always Sized + if struct_datum.binders.map_ref(|b| b.fields.is_empty()).value { + builder.push_fact(trait_ref.clone()); + return; + } + + // To check if a struct type S<..> is Sized, we only have to look at its last field. + // This is because the WF checks for structs require that all the other fields must be Sized. + let last_field_ty = struct_datum + .binders + .map_ref(|b| b.fields.last().unwrap()) + .substitute(interner, substitution) + .clone(); + + let last_field_sized_goal = TraitRef { + trait_id: trait_ref.trait_id, + substitution: Substitution::from1(interner, last_field_ty), + }; + builder.push_clause(trait_ref.clone(), iter::once(last_field_sized_goal)); +} diff --git a/chalk-solve/src/wf.rs b/chalk-solve/src/wf.rs index 10baa4ea588..4a9b49ce668 100644 --- a/chalk-solve/src/wf.rs +++ b/chalk-solve/src/wf.rs @@ -190,6 +190,7 @@ where let wg_goal = gb.forall(&struct_data, (), |gb, _, (fields, where_clauses), ()| { let interner = gb.interner(); + // struct is well-formed in terms of Sized let sized_constraint_goal = compute_struct_sized_constraint(gb.db(), fields); // (FromEnv(T: Eq) => ...) @@ -479,6 +480,9 @@ fn compute_assoc_ty_goal( )) } +/// Computes a goal to prove Sized constraints on a struct definition. +/// Struct is considered well-formed (in terms of Sized) when it either +/// has no fields or all of it's fields except the last are proven to be Sized. fn compute_struct_sized_constraint( db: &dyn RustIrDatabase, fields: &[Ty], From 377e51916634a2efcd92065c3cb7a72ea6627998 Mon Sep 17 00:00:00 2001 From: Mikhail Babenko Date: Tue, 7 Apr 2020 18:48:34 +0300 Subject: [PATCH 4/4] address nits 2: electric boogaloo --- chalk-integration/src/db.rs | 8 +++++--- chalk-integration/src/error.rs | 3 --- chalk-integration/src/lowering.rs | 19 ++++--------------- chalk-integration/src/program.rs | 14 +++++++------- chalk-rust-ir/src/lib.rs | 7 +------ .../src/clauses/builtin_traits/sized.rs | 3 +-- chalk-solve/src/lib.rs | 2 +- chalk-solve/src/wf.rs | 2 +- tests/test/wf_lowering.rs | 15 --------------- 9 files changed, 20 insertions(+), 53 deletions(-) diff --git a/chalk-integration/src/db.rs b/chalk-integration/src/db.rs index 15cabdb80be..e2f1afd913e 100644 --- a/chalk-integration/src/db.rs +++ b/chalk-integration/src/db.rs @@ -21,9 +21,9 @@ use chalk_rust_ir::AssociatedTyDatum; use chalk_rust_ir::AssociatedTyValue; use chalk_rust_ir::AssociatedTyValueId; use chalk_rust_ir::ImplDatum; -use chalk_rust_ir::LangItem; use chalk_rust_ir::StructDatum; use chalk_rust_ir::TraitDatum; +use chalk_rust_ir::WellKnownTrait; use chalk_solve::RustIrDatabase; use chalk_solve::Solution; use chalk_solve::SolverChoice; @@ -138,8 +138,10 @@ impl RustIrDatabase for ChalkDatabase { .impl_provided_for(auto_trait_id, struct_id) } - fn require_lang_item(&self, lang_item: LangItem) -> TraitId { - self.program_ir().unwrap().require_lang_item(lang_item) + fn well_known_trait_id(&self, well_known_trait: WellKnownTrait) -> TraitId { + self.program_ir() + .unwrap() + .well_known_trait_id(well_known_trait) } fn interner(&self) -> &ChalkIr { diff --git a/chalk-integration/src/error.rs b/chalk-integration/src/error.rs index 3cc717c581c..01e2a8d36ca 100644 --- a/chalk-integration/src/error.rs +++ b/chalk-integration/src/error.rs @@ -1,6 +1,5 @@ use chalk_ir::interner::ChalkIr; use chalk_parse::ast::{Identifier, Kind}; -use chalk_rust_ir::LangItem; use chalk_solve::coherence::CoherenceError; use chalk_solve::wf::WfError; @@ -57,7 +56,6 @@ impl std::error::Error for ChalkError {} pub enum RustIrError { InvalidTypeName(Identifier), InvalidLifetimeName(Identifier), - DuplicateLangItem(LangItem), NotTrait(Identifier), NotStruct(Identifier), DuplicateOrShadowedParameters, @@ -100,7 +98,6 @@ impl std::fmt::Display for RustIrError { match self { RustIrError::InvalidTypeName(name) => write!(f, "invalid type name `{}`", name), RustIrError::InvalidLifetimeName(name) => write!(f, "invalid lifetime name `{}`", name), - RustIrError::DuplicateLangItem(item) => write!(f, "duplicate lang item `{:?}`", item), RustIrError::NotTrait(name) => write!( f, "expected a trait, found `{}`, which is not a trait", diff --git a/chalk-integration/src/lowering.rs b/chalk-integration/src/lowering.rs index 64f828cdea7..e6a9d106b89 100644 --- a/chalk-integration/src/lowering.rs +++ b/chalk-integration/src/lowering.rs @@ -3,7 +3,7 @@ use chalk_ir::interner::ChalkIr; use chalk_ir::{self, AssocTypeId, BoundVar, DebruijnIndex, ImplId, StructId, TraitId}; use chalk_parse::ast::*; use chalk_rust_ir as rust_ir; -use chalk_rust_ir::{Anonymize, AssociatedTyValueId, IntoWhereClauses, LangItem, ToParameter}; +use chalk_rust_ir::{Anonymize, AssociatedTyValueId, IntoWhereClauses, ToParameter}; use lalrpop_intern::intern; use std::collections::BTreeMap; use std::sync::Arc; @@ -246,7 +246,7 @@ impl LowerProgram for Program { let mut struct_data = BTreeMap::new(); let mut trait_data = BTreeMap::new(); - let mut trait_lang_items = BTreeMap::new(); + let mut well_known_traits = BTreeMap::new(); let mut impl_data = BTreeMap::new(); let mut associated_ty_data = BTreeMap::new(); let mut associated_ty_values = BTreeMap::new(); @@ -271,18 +271,7 @@ impl LowerProgram for Program { let trait_datum = trait_defn.lower_trait(trait_id, &empty_env)?; if let Some(well_known) = trait_datum.well_known { - if let Some(lang_item) = match well_known { - rust_ir::WellKnownTrait::SizedTrait => Some(LangItem::SizedTrait), - _ => None, - } { - use std::collections::btree_map::Entry; - match trait_lang_items.entry(lang_item) { - Entry::Vacant(vacant) => vacant.insert(trait_id), - Entry::Occupied(_) => { - return Err(RustIrError::DuplicateLangItem(lang_item)) - } - }; - } + well_known_traits.insert(well_known, trait_id); } trait_data.insert(trait_id, Arc::new(trait_datum)); @@ -382,7 +371,7 @@ impl LowerProgram for Program { trait_kinds, struct_data, trait_data, - trait_lang_items, + well_known_traits, impl_data, associated_ty_values, associated_ty_data, diff --git a/chalk-integration/src/program.rs b/chalk-integration/src/program.rs index 229b0a360c8..23adf0c63bd 100644 --- a/chalk-integration/src/program.rs +++ b/chalk-integration/src/program.rs @@ -9,8 +9,8 @@ use chalk_ir::{ TraitId, Ty, TyData, TypeName, }; use chalk_rust_ir::{ - AssociatedTyDatum, AssociatedTyValue, AssociatedTyValueId, ImplDatum, ImplType, LangItem, - StructDatum, TraitDatum, + AssociatedTyDatum, AssociatedTyValue, AssociatedTyValueId, ImplDatum, ImplType, StructDatum, + TraitDatum, WellKnownTrait, }; use chalk_solve::split::Split; use chalk_solve::RustIrDatabase; @@ -46,7 +46,7 @@ pub struct Program { pub trait_data: BTreeMap, Arc>>, /// For each trait lang item - pub trait_lang_items: BTreeMap>, + pub well_known_traits: BTreeMap>, /// For each associated ty declaration `type Foo` found in a trait: pub associated_ty_data: BTreeMap, Arc>>, @@ -312,11 +312,11 @@ impl RustIrDatabase for Program { }) } - fn require_lang_item(&self, lang_item: LangItem) -> TraitId { + fn well_known_trait_id(&self, well_known_trait: WellKnownTrait) -> TraitId { *self - .trait_lang_items - .get(&lang_item) - .unwrap_or_else(|| panic!("No lang item found for {:?}", lang_item)) + .well_known_traits + .get(&well_known_trait) + .unwrap_or_else(|| panic!("No lang item found for {:?}", well_known_trait)) } fn interner(&self) -> &ChalkIr { diff --git a/chalk-rust-ir/src/lib.rs b/chalk-rust-ir/src/lib.rs index 9f33d387132..98280c29b79 100644 --- a/chalk-rust-ir/src/lib.rs +++ b/chalk-rust-ir/src/lib.rs @@ -15,11 +15,6 @@ use chalk_ir::{ }; use std::iter; -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum LangItem { - SizedTrait, -} - /// Identifier for an "associated type value" found in some impl. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct AssociatedTyValueId(pub I::DefId); @@ -136,7 +131,7 @@ pub struct TraitDatum { /// A list of the traits that are "well known" to chalk, which means that /// the chalk-solve crate has special, hard-coded impls for them. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] pub enum WellKnownTrait { SizedTrait, CopyTrait, diff --git a/chalk-solve/src/clauses/builtin_traits/sized.rs b/chalk-solve/src/clauses/builtin_traits/sized.rs index bc7d37a441d..f8349af4f30 100644 --- a/chalk-solve/src/clauses/builtin_traits/sized.rs +++ b/chalk-solve/src/clauses/builtin_traits/sized.rs @@ -35,8 +35,7 @@ pub fn add_sized_program_clauses( let last_field_ty = struct_datum .binders .map_ref(|b| b.fields.last().unwrap()) - .substitute(interner, substitution) - .clone(); + .substitute(interner, substitution); let last_field_sized_goal = TraitRef { trait_id: trait_ref.trait_id, diff --git a/chalk-solve/src/lib.rs b/chalk-solve/src/lib.rs index 68a71019541..cbe7fe0093f 100644 --- a/chalk-solve/src/lib.rs +++ b/chalk-solve/src/lib.rs @@ -77,7 +77,7 @@ pub trait RustIrDatabase: Debug { } /// Returns id of a trait lang item, if found - fn require_lang_item(&self, lang_item: LangItem) -> TraitId; + fn well_known_trait_id(&self, well_known_trait: WellKnownTrait) -> TraitId; fn interner(&self) -> &I; } diff --git a/chalk-solve/src/wf.rs b/chalk-solve/src/wf.rs index 4a9b49ce668..8de5f7bfa34 100644 --- a/chalk-solve/src/wf.rs +++ b/chalk-solve/src/wf.rs @@ -493,7 +493,7 @@ fn compute_struct_sized_constraint( let interner = db.interner(); - let sized_trait = db.require_lang_item(LangItem::SizedTrait); + let sized_trait = db.well_known_trait_id(WellKnownTrait::SizedTrait); Some(Goal::all( interner, diff --git a/tests/test/wf_lowering.rs b/tests/test/wf_lowering.rs index fb53f87055c..8bd93f52f8c 100644 --- a/tests/test/wf_lowering.rs +++ b/tests/test/wf_lowering.rs @@ -637,21 +637,6 @@ fn assoc_type_recursive_bound() { } } -#[test] -fn duplicate_lang_item() { - lowering_error! { - program { - #[lang(sized)] - trait Sized { } - - #[lang(sized)] - trait Sized2 { } - } error_msg { - "duplicate lang item `SizedTrait`" - } - } -} - #[test] fn struct_sized_constraints() { lowering_error! {