Skip to content

🛠 introduce abstraction layer between trait solver and type checker #48895

Closed
@nikomatsakis

Description

@nikomatsakis
Contributor

The purpose of this issue is to setup a trait-solving structure that can change between the current trait solver and the type checker, allowing for us to easily switch to the chalk-style solving when a command line flag is given.

Currently, the interface between the two is the FulfillmentContext, so we have a relationship like this:

[ type checker ] --> [ fulfillment context ] --> [ existing trait solver ]

The first phase then is to introduce another layer in between, let's call it the TraitEngine:

[ type checker ] --> [ TraitEngine ] --> [ fulfillment context ] --> [ existing trait solver ]

When we're done with this phase, everything should work exactly the same, but that the type checker never interacts directly with the fulfillment context.

Activity

nikomatsakis

nikomatsakis commented on Mar 9, 2018

@nikomatsakis
ContributorAuthor

Mentoring instructions (part 1)

How it works today

The main interaction that the type checker has with the fulfillment context is to "register" obligations with it and try to solve them. Registering is primarily done with this method:

pub fn register_predicate_obligation(&mut self,
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
obligation: PredicateObligation<'tcx>)

Solving is done with select_where_possible, which selects obligations when it can, but defers in the case of ambiguity:

pub fn select_where_possible(&mut self,
infcx: &InferCtxt<'a, 'gcx, 'tcx>)
-> Result<(),Vec<FulfillmentError<'tcx>>>

And select_all_or_error, which selects obligations and reports errors if anything comes up as ambiguous (maybe true, maybe false):

pub fn select_all_or_error(&mut self,
infcx: &InferCtxt<'a, 'gcx, 'tcx>)
-> Result<(),Vec<FulfillmentError<'tcx>>>

Step 1: Introduce TraitEngine trait

We should introduce a new module, let's call it rustc::traits::engine. In there would be a trait TraitEngine that (to start) encapsulates a FulfilllmentContext:

pub trait TraitEngine<'tcx> {
    fn register_predicate_obligation(
        &mut self,
        infcx: &InferCtxt<'a, 'gcx, 'tcx>,
        obligation: PredicateObligation<'tcx>,
    );

    // ... mirror the other fulfillment cx methods as needed ...
}

Then we can have FulfillmentContext implement this trait. This could be done by forwarding to the inherent methods, or by moving them out from the inherent impl:

impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
    fn register_predicate_obligation(
        &mut self,
        infcx: &InferCtxt<'a, 'gcx, 'tcx>,
        obligation: PredicateObligation<'tcx>,
    ) {
        // forward to the inherent method
        self.register_predicate_obligation(infcx, obligation);
    }
}

Finally, we create a factory method on TraitEngine; for now it can always create a fulfillment context. The important part is that it returns a Box<dyn TraitEngine<'tcx>>:

impl dyn TraitEngine<'tcx> {
    pub fn new(_tcx: TyCtxt<'_, '_, 'tcx>) -> Box<Self> {
        Box::new(FulfillmentContext::new())
    }
}

Next, we want to modify typeck to use this. For that, we would modify this field from having type RefCell<traits::FulfillmentContext<'tcx>> to RefCell<Box<dyn TraitEngine<'tcx>>>:

fulfillment_cx: RefCell<traits::FulfillmentContext<'tcx>>,

There may be other uses of FulfillmentContext within librustc_typeck, you can ripgrep around. We should be able to convert them all.

Step 2: Add compiler flag and alternative implementation

I'll leave this step for later. =)

added
T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
WG-traits[RETIRED] Working group: Traits
on Mar 9, 2018
nikomatsakis

nikomatsakis commented on Mar 9, 2018

@nikomatsakis
ContributorAuthor

cc @rust-lang/wg-traits -- this is the refactoring step that will allow us to introduce -Zchalk and change implementations. Mentoring instructions are included, looking for someone to pick it up!

sgrif

sgrif commented on Mar 9, 2018

@sgrif
Contributor

This is the sort of structural change that is right up my alley (but I also have a non-zero number of things already on my plate so if someone else is looking for a thing to do, go for it)

added
C-cleanupCategory: PRs that clean code up or issues documenting cleanup.
on Mar 10, 2018
csmoe

csmoe commented on Mar 11, 2018

@csmoe
Member

This work looks not so hard for a rustlang newbie from the mentoring instructions up to now.

If I didn’t underestimate the difficulty, please consider me for this.

nikomatsakis

nikomatsakis commented on Mar 12, 2018

@nikomatsakis
ContributorAuthor

@csmoe I don't think this will be very hard! Please feel free to leave questions here (but expect longer latency) or ping me on gitter (shorter latency)

changed the title [-]introduce abstraction layer between trait solver and type checker[/-] [+]🛠 introduce abstraction layer between trait solver and type checker[/+] on Mar 12, 2018
nikomatsakis

nikomatsakis commented on Mar 20, 2018

@nikomatsakis
ContributorAuthor

@csmoe How goes? Have you had a chance to take a look at this yet? It would be very useful. =)

csmoe

csmoe commented on Mar 20, 2018

@csmoe
Member

@nikomatsakis I have done mostly, but stuck at the lifetime errors.

7 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

A-trait-systemArea: Trait systemC-cleanupCategory: PRs that clean code up or issues documenting cleanup.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.WG-traits[RETIRED] Working group: Traits

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

    Development

    No branches or pull requests

      Participants

      @cuviper@nikomatsakis@sgrif@csmoe

      Issue actions

        🛠 introduce abstraction layer between trait solver and type checker · Issue #48895 · rust-lang/rust