-
Notifications
You must be signed in to change notification settings - Fork 10.5k
SIL: Type lowering for function values with substituted SIL function types. #28065
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@swift-ci Please test |
Build failed |
Build failed |
@swift-ci Please test |
Build failed |
Build failed |
->castTo<ArchetypeType>(); | ||
|
||
// Replace every dependent type we see with a fresh type variable in | ||
// the substituted signature. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is covered correctly in the code, but please clarify in the comment that this also includes opaque types. And that should be tested.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It shouldn't include opaque types, because subst
won't substitute them unless explicitly asked to, and I don't think we need to include them because they aren't arbitrarily substitutable by call sites. I can clarify that in the comment, and add a test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, but they are sometimes substituted, right? On the implementation side of an opaque type, we know that OpaqueType == Int
. If we make opaque-type identity part of the ABI, presumably that means that at some point in SIL on the implementation side we'll need to represent function types where the original type is expressed in terms of OpaqueType
but the concrete type is known to be Int
. Feels easier to me to just define this away.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The opaque type is never formally substituted; we at best do a convert_function or bitcast in places we know the underlying type. Arnold's work on #28044 should more thoroughly handle the relationship between opaque types and their underlying type as part of type lowering, so the difference would only manifest at boundaries between differently-resilient function bodies.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, the ABI of a specific function has to be consistent, so it matters if the representation would say that they're ABI-different on different sides of the resilience boundary. Concretely, if we're going to give () -> (@out Opaque)
a different ptrauth discriminator from () -> (@out Int)
, we need to remember that the function signature was actually written as the former, which is what this work is all about; Arnold's work can't just rewrite it on one side of the boundary and in doing so implicitly change the discriminator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, good point. I'll change it to substitute them out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, doing that correctly in the face of #28044 might require sinking this more deeply into the type lowering logic than I was hoping to start out with, instead of doing it as a post-pass, since the lowered argument and return types would already have the opaque archetypes substituted out of them. If you don't mind, I can do that in a follow up patch after Arnold lands his.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fine, it just needs to happen before we can make ptrauth discriminator tighter.
if (archetype->requiresClass()) { | ||
substRequirements.push_back(Requirement(RequirementKind::Layout,param, | ||
LayoutConstraint::getLayoutConstraint(LayoutConstraintKind::Class))); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know we don't really support any other layout constraints in the language right now, but is there a reason for this not to preserve an arbitrary layout constraint?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had it that way originally, but we apparently do have distinct layout constraints for _NativeClass
and _SingleRefcounted
, which don't seem like useful distinctions from AnyObject
at this level. Maybe it'd be better to specifically canonicalize those internal constraints, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, some of the other layout constraints definitely seem useful to distinguish ABI-wise, and I'm not sure there's harm in just preserving all the original layout constraints. If we specialized an implementation, presumably function arguments would (by default) preserve the original constraints of the function, and it would just be the substitution with the specialized constraint.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main issue I see with that is that base class constraints imply _NativeClass
instead of AnyObject
as their layout constraint when the base class has no ObjC ancestry, and that seems like it could introduce a lot of spurious type mismatches. I can make it so it more selectively generalizes layout constraints, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I hadn't thought of that. Yeah, maybe doing it selectively would make more sense.
…e type in its own generic context
…types. After converting a function type to its corresponding SIL type by our usual rules, extract the substituted generic signature and common interface type for all concrete function types that the function could be called as. We don't currently have any use cases for distinguishing types at finer granularity than generic class-constrained/generic unconstrained/not-generic, so to reduce the amount of conversion noise in SIL, generate the most general generic signature possible, by extracting every position with a dependent generic parameter or associated type thereof into a separate generic parameter in the substituted signature, discarding all constraints except for `AnyObject` layout constraints. This way, if `foo<T>(x: (T, T) -> ())` calls `bar<T, U>(x: (T, U) -> ())` and forwards `x` along, the two closures that only vary in genericity don't need a conversion even with substituted function types, because they'll both have the same abstract lowering `<A, B> (A, B) -> ()` with different substitutions.
1a9ebbd
to
27d7117
Compare
layout->getAlignmentInBits(), | ||
TC.Context); | ||
break; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rjmccall Here's some logic I added to fold up our existing layout constraints into what seemed like the appropriate ABI buckets to me. How's this look?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM.
@swift-ci Please test |
Build failed |
Build failed |
After converting a function type to its corresponding SIL type by our usual rules, extract
the substituted generic signature and common interface type for all concrete function types
that the function could be called as. We don't currently have any use cases for distinguishing
types at finer granularity than generic class-constrained/generic unconstrained/not-generic, so
to reduce the amount of conversion noise in SIL, generate the most general generic signature
possible, by extracting every position with a dependent generic parameter or associated type thereof
into a separate generic parameter in the substituted signature, discarding all constraints except
for
AnyObject
layout constraints. This way, iffoo<T>(x: (T, T) -> ())
callsbar<T, U>(x: (T, U) -> ())
and forwardsx
along, the two closures that only vary in genericitydon't need a conversion even with substituted function types, because they'll both have the same
abstract lowering
<A, B> (A, B) -> ()
with different substitutions.