Skip to content
This repository was archived by the owner on Apr 25, 2025. It is now read-only.
This repository was archived by the owner on Apr 25, 2025. It is now read-only.

Complications with multiple upper bounds #160

Closed
@RossTate

Description

@RossTate

@titzer's Jawa system made use of importing types with multiple upper-bound constraints. More generally, permitting type variables to have multiple upper-bound constraints is often quite useful for low-level type systems. For example, a user-level casting operation often takes the run-time representations of two unknown types (represented as type variables) and returns a boolean that, if equal to true, guarantees that one type variable is a subtype of another, thereby imposing an additional upper bound on the former type variable. (Such an operation is often useful for, say, optimizing multiple assignments into a covariant array in reified polymorphic code: perform one cast of the type parameter to the array's unknown element type, after which all assignments are safe because the two unknowns are known to be subtypes.)

Unfortunately, having multiple upper bounds also causes a number of complications that one needs to plan ahead for if one wants to support this feature. For example, suppose $A has both $B and $C as upper bounds. Suppose $B has the upper bound [] -> [i32] and $C has the upper bound [] -> [f64]. Then what type does call_ref have when the input is a ref $A? i32 or f64? In this case, we might reason that any such function must necessarily be a [] -> ⊥, as the return types are incompatible. But what if instead the upper bounds were [] -> [(ref $B)] and [] -> [(ref $C)] respectively? In that case, the return types are compatible (a function returning ref $A would have both signatures), but there is no way to represent their combined return types. This means the type-checker would have to attempt to type-check the remainder using both possible return types.

This is one of the many subtleties that arises from designing a type system around structural subtyping. For a structural MVP, the likely viable solutions are the following (though I find neither of them to be great):

  1. Permanently annotate all type-directed instructions with the restricted type form(s) to upcast the relevant reference(s) to (like what struct.get currently does) or with the type(s) to upcast the resulting value(s) to.
  2. Permanently disallow multiple upper bounds.

Edit: I had said lower bounds when I meant upper bounds. Post is now correct.

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