Skip to content

Commit 7d0e954

Browse files
committed
refactor well known impl wf checks
1 parent b46ec25 commit 7d0e954

File tree

1 file changed

+125
-97
lines changed

1 file changed

+125
-97
lines changed

chalk-solve/src/wf.rs

Lines changed: 125 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ where
199199
// each variant are sized. For `structs`, we relax this requirement to
200200
// all but the last field.
201201
let sized_constraint_goal =
202-
WfWellKnownGoals::struct_sized_constraint(
202+
WfWellKnownConstraints::struct_sized_constraint(
203203
gb.db(),
204204
fields,
205205
is_enum,
@@ -251,6 +251,10 @@ where
251251
),
252252
);
253253

254+
if let Some(well_known) = self.db.trait_datum(trait_id).well_known {
255+
self.verify_well_known_impl(impl_id, well_known)?
256+
}
257+
254258
debug!("WF trait goal: {:?}", impl_goal);
255259

256260
let mut fresh_solver = (self.solver_builder)();
@@ -317,6 +321,38 @@ where
317321
Err(WfError::IllFormedOpaqueTypeDecl(opaque_ty_id))
318322
}
319323
}
324+
325+
/// Verify builtin rules for well-known traits
326+
pub fn verify_well_known_impl(
327+
&self,
328+
impl_id: ImplId<I>,
329+
well_known: WellKnownTrait,
330+
) -> Result<(), WfError<I>> {
331+
let mut solver = (self.solver_builder)();
332+
let impl_datum = self.db.impl_datum(impl_id);
333+
334+
let is_legal = match well_known {
335+
WellKnownTrait::Copy => {
336+
WfWellKnownConstraints::copy_impl_constraint(&mut *solver, self.db, &impl_datum)
337+
}
338+
WellKnownTrait::Drop => {
339+
WfWellKnownConstraints::drop_impl_constraint(&mut *solver, self.db, &impl_datum)
340+
}
341+
WellKnownTrait::Clone | WellKnownTrait::Unpin => true,
342+
// You can't add a manual implementation for the following traits:
343+
WellKnownTrait::Fn
344+
| WellKnownTrait::FnOnce
345+
| WellKnownTrait::FnMut
346+
| WellKnownTrait::Unsize
347+
| WellKnownTrait::Sized => false,
348+
};
349+
350+
if is_legal {
351+
Ok(())
352+
} else {
353+
Err(WfError::IllFormedTraitImpl(impl_datum.trait_id()))
354+
}
355+
}
320356
}
321357

322358
fn impl_header_wf_goal<I: Interner>(
@@ -338,8 +374,6 @@ fn impl_header_wf_goal<I: Interner>(
338374
let well_formed_goal = gb.forall(&impl_fields, (), |gb, _, (trait_ref, where_clauses), ()| {
339375
let interner = gb.interner();
340376

341-
let trait_constraint_goal = WfWellKnownGoals::inside_impl(gb.db(), &trait_ref);
342-
343377
// if (WC && input types are well formed) { ... }
344378
gb.implies(
345379
impl_wf_environment(interner, &where_clauses, &trait_ref),
@@ -360,20 +394,14 @@ fn impl_header_wf_goal<I: Interner>(
360394
let goals = types
361395
.into_iter()
362396
.map(|ty| ty.well_formed().cast(interner))
363-
.chain(Some((*trait_ref).clone().well_formed().cast(interner)))
364-
.chain(trait_constraint_goal.into_iter());
397+
.chain(Some((*trait_ref).clone().well_formed().cast(interner)));
365398

366399
gb.all::<_, Goal<I>>(goals)
367400
},
368401
)
369402
});
370403

371-
Some(
372-
gb.all(
373-
iter::once(well_formed_goal)
374-
.chain(WfWellKnownGoals::outside_impl(db, &impl_datum).into_iter()),
375-
),
376-
)
404+
Some(well_formed_goal)
377405
}
378406

379407
/// Creates the conditions that an impl (and its contents of an impl)
@@ -537,49 +565,9 @@ fn compute_assoc_ty_goal<I: Interner>(
537565

538566
/// Defines methods to compute well-formedness goals for well-known
539567
/// traits (e.g. a goal for all fields of struct in a Copy impl to be Copy)
540-
struct WfWellKnownGoals {}
541-
542-
impl WfWellKnownGoals {
543-
/// A convenience method to compute the goal assuming `trait_ref`
544-
/// well-formedness requirements are in the environment.
545-
pub fn inside_impl<I: Interner>(
546-
db: &dyn RustIrDatabase<I>,
547-
trait_ref: &TraitRef<I>,
548-
) -> Option<Goal<I>> {
549-
match db.trait_datum(trait_ref.trait_id).well_known? {
550-
WellKnownTrait::Copy => Self::copy_impl_constraint(db, trait_ref),
551-
WellKnownTrait::Drop
552-
| WellKnownTrait::Clone
553-
| WellKnownTrait::Sized
554-
| WellKnownTrait::FnOnce
555-
| WellKnownTrait::FnMut
556-
| WellKnownTrait::Fn
557-
| WellKnownTrait::Unsize
558-
| WellKnownTrait::Unpin => None,
559-
}
560-
}
561-
562-
/// Computes well-formedness goals without any assumptions about the environment.
563-
/// Note that `outside_impl` does not call `inside_impl`, one needs to call both
564-
/// in order to get the full set of goals to be proven.
565-
pub fn outside_impl<I: Interner>(
566-
db: &dyn RustIrDatabase<I>,
567-
impl_datum: &ImplDatum<I>,
568-
) -> Option<Goal<I>> {
569-
let interner = db.interner();
570-
571-
match db.trait_datum(impl_datum.trait_id()).well_known? {
572-
WellKnownTrait::Drop => Self::drop_impl_constraint(db, impl_datum),
573-
WellKnownTrait::Copy | WellKnownTrait::Clone | WellKnownTrait::Unpin => None,
574-
// You can't add a manual implementation for following traits:
575-
WellKnownTrait::Sized
576-
| WellKnownTrait::FnOnce
577-
| WellKnownTrait::FnMut
578-
| WellKnownTrait::Fn
579-
| WellKnownTrait::Unsize => Some(GoalData::CannotProve.intern(interner)),
580-
}
581-
}
568+
struct WfWellKnownConstraints {}
582569

570+
impl WfWellKnownConstraints {
583571
/// Computes a goal to prove Sized constraints on a struct definition.
584572
/// Struct is considered well-formed (in terms of Sized) when it either
585573
/// has no fields or all of it's fields except the last are proven to be Sized.
@@ -610,71 +598,108 @@ impl WfWellKnownGoals {
610598
))
611599
}
612600

613-
/// Computes a goal to prove constraints on a Copy implementation.
601+
/// Verify constraints on a Copy implementation.
614602
/// Copy impl is considered well-formed for
615603
/// a) certain builtin types (scalar values, shared ref, etc..)
616604
/// b) adts which
617605
/// 1) have all Copy fields
618606
/// 2) don't have a Drop impl
619607
fn copy_impl_constraint<I: Interner>(
608+
solver: &mut dyn Solver<I>,
620609
db: &dyn RustIrDatabase<I>,
621-
trait_ref: &TraitRef<I>,
622-
) -> Option<Goal<I>> {
610+
impl_datum: &ImplDatum<I>,
611+
) -> bool {
623612
let interner = db.interner();
624613

625-
let ty = trait_ref.self_type_parameter(interner);
626-
let ty_data = ty.data(interner);
614+
let mut gb = GoalBuilder::new(db);
615+
616+
let impl_fields = impl_datum
617+
.binders
618+
.map_ref(|v| (&v.trait_ref, &v.where_clauses));
627619

628620
// Implementations for scalars, pointer types and never type are provided by libcore.
629621
// User implementations on types other than ADTs are forbidden.
630-
let (adt_id, substitution) = match ty_data {
631-
TyData::Apply(ApplicationTy { name, substitution }) => match name {
622+
match impl_datum
623+
.binders
624+
.skip_binders()
625+
.trait_ref
626+
.self_type_parameter(interner)
627+
.data(interner)
628+
{
629+
TyData::Apply(ApplicationTy { name, .. }) => match name {
632630
TypeName::Scalar(_)
633631
| TypeName::Raw(_)
634632
| TypeName::Ref(Mutability::Not)
635-
| TypeName::Never => return None,
636-
TypeName::Adt(adt_id) => (*adt_id, substitution),
637-
_ => return Some(GoalData::CannotProve.intern(interner)),
633+
| TypeName::Never => return true,
634+
TypeName::Adt(_) => (),
635+
_ => return false,
638636
},
639637

640-
_ => return Some(GoalData::CannotProve.intern(interner)),
638+
_ => return false,
641639
};
642640

643-
// not { Implemented(ImplSelfTy: Drop) }
644-
let neg_drop_goal = db
645-
.well_known_trait_id(WellKnownTrait::Drop)
646-
.map(|drop_trait_id| {
647-
TraitRef {
648-
trait_id: drop_trait_id,
649-
substitution: Substitution::from1(interner, ty.clone()),
650-
}
651-
.cast::<Goal<I>>(interner)
652-
.negate(interner)
653-
});
641+
// Well fomedness goal for ADTs
642+
let well_formed_goal =
643+
gb.forall(&impl_fields, (), |gb, _, (trait_ref, where_clauses), ()| {
644+
let interner = gb.interner();
654645

655-
let adt_datum = db.adt_datum(adt_id);
646+
let ty = trait_ref.self_type_parameter(interner);
647+
let ty_data = ty.data(interner);
656648

657-
let goals = adt_datum
658-
.binders
659-
.map_ref(|b| &b.variants)
660-
.substitute(interner, substitution)
661-
.into_iter()
662-
.flat_map(|v| {
663-
v.fields.into_iter().map(|f| {
664-
// Implemented(FieldTy: Copy)
665-
TraitRef {
666-
trait_id: trait_ref.trait_id,
667-
substitution: Substitution::from1(interner, f),
668-
}
669-
.cast(interner)
670-
})
671-
})
672-
.chain(neg_drop_goal.into_iter());
649+
let (adt_id, substitution) = match ty_data {
650+
TyData::Apply(ApplicationTy { name, substitution }) => match name {
651+
TypeName::Adt(adt_id) => (*adt_id, substitution),
652+
_ => unreachable!(),
653+
},
654+
655+
_ => unreachable!(),
656+
};
657+
658+
// if (WC) { ... }
659+
gb.implies(
660+
impl_wf_environment(interner, &where_clauses, &trait_ref),
661+
|gb| -> Goal<I> {
662+
let db = gb.db();
663+
664+
// not { Implemented(ImplSelfTy: Drop) }
665+
let neg_drop_goal =
666+
db.well_known_trait_id(WellKnownTrait::Drop)
667+
.map(|drop_trait_id| {
668+
TraitRef {
669+
trait_id: drop_trait_id,
670+
substitution: Substitution::from1(interner, ty.clone()),
671+
}
672+
.cast::<Goal<I>>(interner)
673+
.negate(interner)
674+
});
675+
676+
let adt_datum = db.adt_datum(adt_id);
677+
678+
let goals = adt_datum
679+
.binders
680+
.map_ref(|b| &b.variants)
681+
.substitute(interner, substitution)
682+
.into_iter()
683+
.flat_map(|v| {
684+
v.fields.into_iter().map(|f| {
685+
// Implemented(FieldTy: Copy)
686+
TraitRef {
687+
trait_id: trait_ref.trait_id,
688+
substitution: Substitution::from1(interner, f),
689+
}
690+
.cast(interner)
691+
})
692+
})
693+
.chain(neg_drop_goal.into_iter());
694+
gb.all(goals)
695+
},
696+
)
697+
});
673698

674-
Some(Goal::all(interner, goals))
699+
solver.has_unique_solution(db, &well_formed_goal.into_closed_goal(interner))
675700
}
676701

677-
/// Computes goal to prove constraints on a Drop implementation
702+
/// Verifies constraints on a Drop implementation
678703
/// Drop implementation is considered well-formed if:
679704
/// a) it's implemented on an ADT
680705
/// b) The generic parameters of the impl's type must all be parameters
@@ -709,15 +734,16 @@ impl WfWellKnownGoals {
709734
/// }
710735
/// ```
711736
fn drop_impl_constraint<I: Interner>(
737+
solver: &mut dyn Solver<I>,
712738
db: &dyn RustIrDatabase<I>,
713739
impl_datum: &ImplDatum<I>,
714-
) -> Option<Goal<I>> {
740+
) -> bool {
715741
let interner = db.interner();
716742

717743
let adt_id = match impl_datum.self_type_adt_id(interner) {
718744
Some(id) => id,
719745
// Drop can only be implemented on a nominal type
720-
None => return Some(GoalData::CannotProve.intern(interner)),
746+
None => return false,
721747
};
722748

723749
let mut gb = GoalBuilder::new(db);
@@ -783,6 +809,8 @@ impl WfWellKnownGoals {
783809
},
784810
);
785811

786-
Some(gb.all([implied_by_adt_def_goal, eq_goal].iter()))
812+
let well_formed_goal = gb.all([implied_by_adt_def_goal, eq_goal].iter());
813+
814+
solver.has_unique_solution(db, &well_formed_goal.into_closed_goal(interner))
787815
}
788816
}

0 commit comments

Comments
 (0)