Skip to content

Commit 73a74be

Browse files
authored
Merge pull request #374 from Areredify/sized
add Sized well-known impl and wf check
2 parents 31ea6d8 + 377e519 commit 73a74be

File tree

12 files changed

+201
-19
lines changed

12 files changed

+201
-19
lines changed

chalk-integration/src/db.rs

+7
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use chalk_rust_ir::AssociatedTyValueId;
2323
use chalk_rust_ir::ImplDatum;
2424
use chalk_rust_ir::StructDatum;
2525
use chalk_rust_ir::TraitDatum;
26+
use chalk_rust_ir::WellKnownTrait;
2627
use chalk_solve::RustIrDatabase;
2728
use chalk_solve::Solution;
2829
use chalk_solve::SolverChoice;
@@ -137,6 +138,12 @@ impl RustIrDatabase<ChalkIr> for ChalkDatabase {
137138
.impl_provided_for(auto_trait_id, struct_id)
138139
}
139140

141+
fn well_known_trait_id(&self, well_known_trait: WellKnownTrait) -> TraitId<ChalkIr> {
142+
self.program_ir()
143+
.unwrap()
144+
.well_known_trait_id(well_known_trait)
145+
}
146+
140147
fn interner(&self) -> &ChalkIr {
141148
&ChalkIr
142149
}

chalk-integration/src/error.rs

-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use chalk_ir::interner::ChalkIr;
22
use chalk_parse::ast::{Identifier, Kind};
3-
use chalk_rust_ir::LangItem;
43
use chalk_solve::coherence::CoherenceError;
54
use chalk_solve::wf::WfError;
65

@@ -57,7 +56,6 @@ impl std::error::Error for ChalkError {}
5756
pub enum RustIrError {
5857
InvalidTypeName(Identifier),
5958
InvalidLifetimeName(Identifier),
60-
DuplicateLangItem(LangItem),
6159
NotTrait(Identifier),
6260
NotStruct(Identifier),
6361
DuplicateOrShadowedParameters,
@@ -100,7 +98,6 @@ impl std::fmt::Display for RustIrError {
10098
match self {
10199
RustIrError::InvalidTypeName(name) => write!(f, "invalid type name `{}`", name),
102100
RustIrError::InvalidLifetimeName(name) => write!(f, "invalid lifetime name `{}`", name),
103-
RustIrError::DuplicateLangItem(item) => write!(f, "duplicate lang item `{:?}`", item),
104101
RustIrError::NotTrait(name) => write!(
105102
f,
106103
"expected a trait, found `{}`, which is not a trait",

chalk-integration/src/lowering.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ impl LowerProgram for Program {
249249

250250
let mut struct_data = BTreeMap::new();
251251
let mut trait_data = BTreeMap::new();
252+
let mut well_known_traits = BTreeMap::new();
252253
let mut impl_data = BTreeMap::new();
253254
let mut associated_ty_data = BTreeMap::new();
254255
let mut associated_ty_values = BTreeMap::new();
@@ -270,10 +271,13 @@ impl LowerProgram for Program {
270271
}
271272
Item::TraitDefn(ref trait_defn) => {
272273
let trait_id = TraitId(raw_id);
273-
trait_data.insert(
274-
trait_id,
275-
Arc::new(trait_defn.lower_trait(trait_id, &empty_env)?),
276-
);
274+
let trait_datum = trait_defn.lower_trait(trait_id, &empty_env)?;
275+
276+
if let Some(well_known) = trait_datum.well_known {
277+
well_known_traits.insert(well_known, trait_id);
278+
}
279+
280+
trait_data.insert(trait_id, Arc::new(trait_datum));
277281

278282
for assoc_ty_defn in &trait_defn.assoc_ty_defns {
279283
let lookup = &associated_ty_lookups[&(trait_id, assoc_ty_defn.name.str)];
@@ -370,6 +374,7 @@ impl LowerProgram for Program {
370374
trait_kinds,
371375
struct_data,
372376
trait_data,
377+
well_known_traits,
373378
impl_data,
374379
associated_ty_values,
375380
associated_ty_data,

chalk-integration/src/program.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use chalk_ir::{
1010
};
1111
use chalk_rust_ir::{
1212
AssociatedTyDatum, AssociatedTyValue, AssociatedTyValueId, ImplDatum, ImplType, StructDatum,
13-
TraitDatum,
13+
TraitDatum, WellKnownTrait,
1414
};
1515
use chalk_solve::split::Split;
1616
use chalk_solve::RustIrDatabase;
@@ -45,6 +45,9 @@ pub struct Program {
4545
/// For each trait:
4646
pub trait_data: BTreeMap<TraitId<ChalkIr>, Arc<TraitDatum<ChalkIr>>>,
4747

48+
/// For each trait lang item
49+
pub well_known_traits: BTreeMap<WellKnownTrait, TraitId<ChalkIr>>,
50+
4851
/// For each associated ty declaration `type Foo` found in a trait:
4952
pub associated_ty_data: BTreeMap<AssocTypeId<ChalkIr>, Arc<AssociatedTyDatum<ChalkIr>>>,
5053

@@ -318,6 +321,13 @@ impl RustIrDatabase<ChalkIr> for Program {
318321
})
319322
}
320323

324+
fn well_known_trait_id(&self, well_known_trait: WellKnownTrait) -> TraitId<ChalkIr> {
325+
*self
326+
.well_known_traits
327+
.get(&well_known_trait)
328+
.unwrap_or_else(|| panic!("No lang item found for {:?}", well_known_trait))
329+
}
330+
321331
fn interner(&self) -> &ChalkIr {
322332
&ChalkIr
323333
}

chalk-integration/src/query.rs

-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,6 @@ fn checked_program(db: &impl LoweringDatabase) -> Result<Arc<Program>, ChalkErro
100100

101101
let () = tls::set_current_program(&program, || -> Result<(), ChalkError> {
102102
let solver = wf::WfSolver::new(db, db.solver_choice());
103-
104103
for &id in program.struct_data.keys() {
105104
solver.verify_struct_decl(id)?;
106105
}

chalk-rust-ir/src/lib.rs

+2-5
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@ use chalk_ir::{
1515
};
1616
use std::iter;
1717

18-
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
19-
pub enum LangItem {}
20-
2118
/// Identifier for an "associated type value" found in some impl.
2219
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
2320
pub struct AssociatedTyValueId<I: Interner>(pub I::DefId);
@@ -78,7 +75,7 @@ impl<I: Interner> StructDatum<I> {
7875
}
7976
}
8077

81-
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
78+
#[derive(Clone, Debug, PartialEq, Eq, Hash, Fold, HasInterner)]
8279
pub struct StructDatumBound<I: Interner> {
8380
pub fields: Vec<Ty<I>>,
8481
pub where_clauses: Vec<QuantifiedWhereClause<I>>,
@@ -134,7 +131,7 @@ pub struct TraitDatum<I: Interner> {
134131

135132
/// A list of the traits that are "well known" to chalk, which means that
136133
/// the chalk-solve crate has special, hard-coded impls for them.
137-
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
134+
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
138135
pub enum WellKnownTrait {
139136
SizedTrait,
140137
CopyTrait,

chalk-solve/src/clauses/builtin_traits.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ use super::builder::ClauseBuilder;
22
use crate::{Interner, RustIrDatabase, TraitRef, WellKnownTrait};
33
use chalk_ir::TyData;
44

5+
mod sized;
6+
57
/// For well known traits we have special hard-coded impls, either as an
68
/// optimization or to enforce special rules for correctness.
79
pub fn add_builtin_program_clauses<I: Interner>(
@@ -16,7 +18,7 @@ pub fn add_builtin_program_clauses<I: Interner>(
1618
}
1719

1820
match well_known {
19-
WellKnownTrait::SizedTrait => { /* TODO */ }
21+
WellKnownTrait::SizedTrait => sized::add_sized_program_clauses(db, builder, trait_ref, ty),
2022
WellKnownTrait::CopyTrait => { /* TODO */ }
2123
WellKnownTrait::CloneTrait => { /* TODO */ }
2224
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
use std::iter;
2+
3+
use crate::clauses::ClauseBuilder;
4+
use crate::{Interner, RustIrDatabase, TraitRef};
5+
use chalk_ir::{ApplicationTy, Substitution, TyData, TypeName};
6+
7+
pub fn add_sized_program_clauses<I: Interner>(
8+
db: &dyn RustIrDatabase<I>,
9+
builder: &mut ClauseBuilder<'_, I>,
10+
trait_ref: &TraitRef<I>,
11+
ty: &TyData<I>,
12+
) {
13+
let interner = db.interner();
14+
15+
let (struct_id, substitution) = match ty {
16+
TyData::Apply(ApplicationTy {
17+
name: TypeName::Struct(struct_id),
18+
substitution,
19+
}) => (*struct_id, substitution),
20+
// TODO(areredify)
21+
// when #368 lands, extend this to handle everything accordingly
22+
_ => return,
23+
};
24+
25+
let struct_datum = db.struct_datum(struct_id);
26+
27+
// Structs with no fields are always Sized
28+
if struct_datum.binders.map_ref(|b| b.fields.is_empty()).value {
29+
builder.push_fact(trait_ref.clone());
30+
return;
31+
}
32+
33+
// To check if a struct type S<..> is Sized, we only have to look at its last field.
34+
// This is because the WF checks for structs require that all the other fields must be Sized.
35+
let last_field_ty = struct_datum
36+
.binders
37+
.map_ref(|b| b.fields.last().unwrap())
38+
.substitute(interner, substitution);
39+
40+
let last_field_sized_goal = TraitRef {
41+
trait_id: trait_ref.trait_id,
42+
substitution: Substitution::from1(interner, last_field_ty),
43+
};
44+
builder.push_clause(trait_ref.clone(), iter::once(last_field_sized_goal));
45+
}

chalk-solve/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ pub trait RustIrDatabase<I: Interner>: Debug {
7676
false
7777
}
7878

79+
/// Returns id of a trait lang item, if found
80+
fn well_known_trait_id(&self, well_known_trait: WellKnownTrait) -> TraitId<I>;
81+
7982
fn interner(&self) -> &I;
8083
}
8184

chalk-solve/src/wf.rs

+45-3
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,9 @@ where
196196
let wg_goal = gb.forall(&struct_data, (), |gb, _, (fields, where_clauses), ()| {
197197
let interner = gb.interner();
198198

199+
// struct is well-formed in terms of Sized
200+
let sized_constraint_goal = compute_struct_sized_constraint(gb.db(), fields);
201+
199202
// (FromEnv(T: Eq) => ...)
200203
gb.implies(
201204
where_clauses
@@ -209,7 +212,13 @@ where
209212
fields.fold(gb.interner(), &mut input_types);
210213
// ...in a where clause.
211214
where_clauses.fold(gb.interner(), &mut input_types);
212-
gb.all(input_types.into_iter().map(|ty| ty.well_formed()))
215+
216+
gb.all(
217+
input_types
218+
.into_iter()
219+
.map(|ty| ty.well_formed().cast(interner))
220+
.chain(sized_constraint_goal.into_iter()),
221+
)
213222
},
214223
)
215224
});
@@ -230,7 +239,14 @@ where
230239

231240
pub fn verify_trait_impl(&self, impl_id: ImplId<I>) -> Result<(), WfError<I>> {
232241
let interner = self.db.interner();
242+
233243
let impl_datum = self.db.impl_datum(impl_id);
244+
let trait_id = impl_datum.trait_id();
245+
246+
// You can't manually implement Sized
247+
if let Some(WellKnownTrait::SizedTrait) = self.db.trait_datum(trait_id).well_known {
248+
return Err(WfError::IllFormedTraitImpl(trait_id));
249+
}
234250

235251
let impl_goal = Goal::all(
236252
interner,
@@ -256,8 +272,7 @@ where
256272
if is_legal {
257273
Ok(())
258274
} else {
259-
let trait_ref = &impl_datum.binders.value.trait_ref;
260-
Err(WfError::IllFormedTraitImpl(trait_ref.trait_id))
275+
Err(WfError::IllFormedTraitImpl(trait_id))
261276
}
262277
}
263278
}
@@ -470,3 +485,30 @@ fn compute_assoc_ty_goal<I: Interner>(
470485
},
471486
))
472487
}
488+
489+
/// Computes a goal to prove Sized constraints on a struct definition.
490+
/// Struct is considered well-formed (in terms of Sized) when it either
491+
/// has no fields or all of it's fields except the last are proven to be Sized.
492+
fn compute_struct_sized_constraint<I: Interner>(
493+
db: &dyn RustIrDatabase<I>,
494+
fields: &[Ty<I>],
495+
) -> Option<Goal<I>> {
496+
if fields.len() <= 1 {
497+
return None;
498+
}
499+
500+
let interner = db.interner();
501+
502+
let sized_trait = db.well_known_trait_id(WellKnownTrait::SizedTrait);
503+
504+
Some(Goal::all(
505+
interner,
506+
fields[..fields.len() - 1].iter().map(|ty| {
507+
TraitRef {
508+
trait_id: sized_trait,
509+
substitution: Substitution::from1(interner, ty.clone()),
510+
}
511+
.cast(interner)
512+
}),
513+
))
514+
}

tests/test/auto_traits.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@ use super::*;
66
fn auto_semantics() {
77
test! {
88
program {
9+
#[lang(sized)] trait Sized { }
910
#[auto] trait Send { }
1011

1112
struct i32 { }
1213

1314
struct Ptr<T> { }
1415
impl<T> Send for Ptr<T> where T: Send { }
1516

16-
struct List<T> {
17+
struct List<T> where T: Sized {
1718
data: T,
1819
next: Ptr<List<T>>
1920
}

tests/test/wf_lowering.rs

+74
Original file line numberDiff line numberDiff line change
@@ -636,3 +636,77 @@ fn assoc_type_recursive_bound() {
636636
}
637637
}
638638
}
639+
640+
#[test]
641+
fn struct_sized_constraints() {
642+
lowering_error! {
643+
program {
644+
#[lang(sized)]
645+
trait Sized { }
646+
647+
struct S<T> {
648+
t1: T,
649+
t2: T
650+
}
651+
} error_msg {
652+
"type declaration `S` does not meet well-formedness requirements"
653+
}
654+
}
655+
656+
lowering_success! {
657+
program {
658+
#[lang(sized)]
659+
trait Sized { }
660+
661+
struct Foo { }
662+
663+
struct S<T> {
664+
t1: Foo,
665+
t2: T
666+
}
667+
}
668+
}
669+
670+
lowering_success! {
671+
program {
672+
#[lang(sized)]
673+
trait Sized { }
674+
675+
struct S<T> where T: Sized {
676+
t1: T,
677+
t2: T
678+
}
679+
}
680+
}
681+
682+
lowering_success! {
683+
program {
684+
#[lang(sized)]
685+
trait Sized { }
686+
687+
struct Foo {}
688+
689+
struct G<T> {
690+
foo: S<S<Foo>>,
691+
s: S<S<S<T>>>
692+
}
693+
694+
struct S<T> {
695+
t1: T
696+
}
697+
}
698+
}
699+
700+
lowering_error! {
701+
program {
702+
#[lang(sized)]
703+
trait Sized { }
704+
705+
struct Foo {}
706+
707+
impl Sized for Foo {}
708+
} error_msg {
709+
"trait impl for `Sized` does not meet well-formedness requirements"
710+
}
711+
}
712+
}

0 commit comments

Comments
 (0)