Mixture of CanTypes and SILTypes in SIL

Is it accidental or purposeful that SIL sometimes freely mixes CanTypes and SILTypes? For example, a random SIL constructor:

AllocExistentialBoxInst(SILDebugLocation DebugLoc, SILType ExistentialType,
                          CanType ConcreteType,
                          ArrayRef<ProtocolConformanceRef> Conformances,
                          ArrayRef<SILValue> TypeDependentOperands,
                          SILFunction *Parent)

Would anybody object if examples like the above were converted to consistently use SILType?

CC: @Joe_Groff and @Michael_Gottesman

The uses of CanType generally represent "formal types", i.e. language-level types rather than representational SIL types. That's the case here. It would be actively incorrect to convert these to store representational types.

2 Likes

Interesting. I thought SILType was just about type lowering but I keep bumping into cases where that’s not true. Thanks

SILType is about type lowering, yes. A formal type like (Int) -> Any is lowered to a SILType like (Int) -> @out Any, and it isn't necessarily straightforward (or possible) to reverse that lowering to get back to the formal type, so the places that semantically need to preserve formal types have to pass that information around. alloc_existential_box is one of those places because it has to store the formal type of the value captured in the box.

Are formal types merely the pre-lowered type? Or has some non-destructive processing been done like assigning a representation to the metatypes?

It's the pre-lowered type.

I've filed [SR-13330] [docs] Document why CanTypes and SILTypes mix in SIL · Issue #55770 · apple/swift · GitHub to track updating the documentation to answer this.

1 Like

Is there any particular reason why CanType is used for both formal types and lowered types (given how pervasive SILType::getASTType is used)? Or is this just technical debt?

Would a third type not be helpful here? For example: "LoweredType" could statically represent a lowered type that hasn't been assigned a SIL representation yet. Then the constructor to SILType could require a LoweredType instead of a CanType and the dynamic "is legal SIL type" check could go away.

What if anything am I missing?

This seems correct. Here are snippets from a conversation I had with @Slava_Pestov and @John_McCall about a year back:

John: getASTType() is an unfortunate name, or (arguably) our sometimes use of "AST type" to mean "formal type" is unfortunate, or (doubly arguably) our use of the same AST type system for lowered types is unfortunate.

[...]

Slava: ideally, SILType would have a tri-state discriminator, or getASTType() would return a LoweredType wrapper, and we would ban SILFunctionType from being stored in CanType or Type containers

John: Yeah, a LoweredType wrapper that aligns with CanType would be useful.

John: Basically CanType but differently colored.

1 Like

Let's take another example: InitExistentialMetatypeInst::getFormalErasedObjectType() has "formal" in the name and returns a CanType but the result might be a lowered SIL type and therefore not a "formal" type. Is the method name wrong? Or is this a modeling bug because getASTType() returns CanType instead of a hypothetical "LoweredType"?

(I'm far from an expert in this area of the compiler but:) I think it is the former, the method was named in 2014.

A SILType is a lowered formal type together with a "category" (object or address). CanType is used for both formal types, and lowered types where the category does not matter.

Changing that at this point would create a huge amount of churn and I'm not sure it is worth it.