Skip to content

subset errors #153

@nikomatsakis

Description

@nikomatsakis

So @lqd and I had a big discussion today and resolved the major open questions with respect to reporting "subset errors". In short, we decided to adopt the approach laid out in the original smallcultfollowing blog post, but with a bunch of other details. =) This issue tracks the implementation of that work and explains the rationale.

The problem

Rust functions contain "placeholder origins" like 'a and 'b in this example:

fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x }

We want this code to be an error. But the error is not due to an illegal access error -- it is because data with the origin 'a is returned with the origin 'b, and there is no 'a: 'b where clause.

Alternative A: Use subsets and declare errors

The original blog post proposed an approach to solving this problem where we looking for a subst relation subset('a, 'b, _) that involves two placeholder origins 'a and 'b, and then compare that against a set of "known subsets" (derived from the where clauses). If we find a subset relation that is not known to be valid, that's an error. This can (ultra naively) be expressed with a single rule:

subset_error(Origin1, Origin2) :-
    subset(Origin1, Origin2, _),
    placeholder_region(Origin1),
    placeholder_region(Origin2),
    !known_subset(Origin1, Origin2).

Alternative B

The main competitor that we were considering was the idea of using 'placeholder loans'. In this approach, for each placeholder origin, we have a corresponding placeholder loan. We can then compute the subset and contains relations as normal, and declare an error if a placeholder loan finds its way into a placeholder origin where it doesn't belong (i.e., in the above example, the placeholder origin for 'b would contain the loan for 'a). So the error rule would like something like this:

subset_error(Origin1, Origin2) :-
    contains(Origin2, Loan1, _),
    placeholder_origin(Origin1, Loan1),
    placeholder_origin(Origin2, Loan2),
    !known_subset(Origin1, Origin2).

but we also need some rules like

contains(Origin1, Loan1, Node) :-
    cfg_node(Node),
    placeholder_origin(Origin1, Loan1).

Consideration: Elegance

Naturally we want to find relatively minimal rules that are clean and easy to understand. The subset approach largely wins on this approach, but as we'll see later there are a few complications.

Consideration: Origins are just sets of loans, right?

One of the original motivations for pursuing the "placeholder loan" approach was that we wanted to be able to treat origins as if they were just sets of loans and didn't have an identity of their own. However, in practice, we can't quite do this anyway, because we have to track subset relations forward through the graph (see #107 for related discussion about that). So it seems like either way origins have some identity of their own in the polonius framework.

Consideration: Location Insensitive analysis

One of the things we were considering is the ability to optimize the naive and optimized analysis based on a preliminary location insensitive run. This location insensitive run will produce results that can be used to limit the transitive closure computations and make everything more performant.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions