Clarify what can and cannot be done with a incomplete interface or named constraint.
For purposes of this proposal, the term constraints refers to interfaces and named constraints. Interfaces do not require any different treatment from named constraints, and treating them the same is good for simplicity and uniformity.
We want to support forward declaring constraints so that they may be used before they are allowed to be defined, due to recursion or a reference cycle. Some uses require the definition of the constraint to be visible to be checked, however. In some cases, those checks may be delayed at the cost of complexity in the implementation of the compiler. This proposal aims to specify what uses are allowed, particularly those cases that were not covered by the previous proposal on forward declarations.
Proposal #1084: Generics details 9: forward declarations originally laid out the rules for incomplete constraints. Since then, the discussion on 2022-10-12 considered how we needed to update and fill in those rules, as part of an effort to implement those rules in the Carbon Explorer.
The new rule details have been added by this proposal PR to the Forward declarations and cyclic references: declaring interfaces and named constraints section of Generics: details design document.
This proposal is balancing the expressivity needed to make code easier to write with implementation simplicity, both concerns of Carbon's "code that is easy to read, understand, and write" goal.
In particular, we considered allowing the declaration of an interface or named constraint in an API file and the definition in the impl file of the same library. We considered some use cases that might benefit from this in #931 and #971. In #generics-and-templates on 2022-10-24, we decided those use cases would be okay including the definition of the private interface in the API file. So to support checking for invalid uses of an incomplete interfaces with information local to that file, we reaffirmed the decision in #971 and implemented in proposal #1084 to require the definition to be in the same file as the declaration.
where clauses on incomplete constraintsWe considered not having a specific rule preventing any where clause on an
incomplete constraint. The idea is that the problematic cases, such as those
that access members of the constraint like where .X = T, are already
forbidden. By removing this rule, we would allow constructions like
C where Vector(.Self) is Hashable for incomplete C. We decided to wait until
we had a motivating use case to allow this, and start with the more restrictive
rule.
C & DWe considered forbidding the combination of incomplete constraints with the &
operator, because otherwise we would not be able to diagnose conflicts between
the two constraints. For example,
constraint C;
constraint D;
// Can't tell if there is a conflict between `C` and `D`.
fn F[T:! C & D](x: T);
// These definitions of `C` and `D` conflict.
constraint C {
extends I where .X = i32;
}
constraint D {
extends I where .X = bool;
}
Since our existing examples of using forward declarations to define a graph with cyclic references between the edge and node interfaces needed this feature, we decided to support it. This motivated the requirement that the definition be in the same file as the declaration, so the error could be detected once the compiler reached the definitions.