SILGen: Fixes for *static* 'Self'-returning methods

Take a seat and pour yourself a beer because this is
going to get pretty intense.

Recall that class methods that return 'Self', have a
'self' type of @dynamic_self X or @dynamic_self X.Type,
for some class X, based on if the method is an instance
method or a static method.

The instance type of a metatype is not lowered, and we
preserve DynamicSelfType there. This is required for
correct behavior with the SIL optimizer.

For example if you specialize a function that contains a
'metatype $((T) -> Int, T).Type' SIL instruction or
some other metatype of a structural type containing a
generic parameter, we might end up with something like
'metatype $((@dynamic_self X) -> Int, X).Type'
after substitution, for some class 'X'. Note that the
second occurrence of 'X', is in "lowered position" so
the @dynamic_self did, indeed, get stripped away.

So while *values* of @dynamic_self type don't need to
carry the fact that they're @dynamic_self at the SIL
level, because Sema has inserted all the right casts.

Metatypes do though, because when lowering the 'metatype'
instruction, IRGen has to know to emit the type metadata
from the method's 'self' parameter, and not the static
metadata for the exact class type.

Essentially, 'metatype @dynamic_self X.Type' is
the same as 'value_metatype %self : X.Type', except that
the @dynamic_self type can appear inside other structural
types also, which is something we cannot write in the
AST.

This is all well and good, but when lowering a
SILFunctionType we erase @dynamic_self from the 'self'
parameter type because when you *call* such a function
from another function, you are not necessarily calling
it on your own 'self' value. And if you are, Sema
already emitted the right unchecked downcast there to
turn the result into the right type.

The problem is that the type of an argument (the value
"inside" the function) used to always be identical to
the type of the parameter (the type from "outside" the
function, in the SILFunctionType). Of course this
assumption is no longer correct for static methods,
where the 'self' argument should really have type
@dynamic_self X.Type, not X.Type.

A further complication is closure captures, whose types
can also contain @dynamic_self inside metatypes in other
structural types. We used to erase @dynamic_self from
these.

Both of these are wrong, because if you call a generic
function <T> (T.Type) -> () with a T := @dynamic_self X
substitution (recall that substitutions are written in
terms of AST types and not lowered types) and pass in
the 'self' argument, we would pass in a value of type
X.Type and not @dynamic_self X.Type.

There were similar issues with captures, with
additional complications from nested closures.

Fix all this by having SILGenProlog emit a downcast
to turn the X.Type argument into a value of type
@dynamic_self X.Type, and tweak capture lowering to
not erase @dynamic_self from capture types.

This fixes several cases that used to fail with
asserts in SILGenApply or the SIL verifier, in particular
the example outlined in <rdar://problem/31226650>,
where we would crash when calling a protocol extension
method from a static class method (oops!).

If you got this far and still follow along,
congratulations, you now know more about DynamicSelfType
than I do.
This commit is contained in:
Slava Pestov
2017-03-26 19:55:43 -07:00
parent 0e7316d3d0
commit 35a5594035
8 changed files with 120 additions and 72 deletions

View File

@@ -2678,9 +2678,13 @@ public:
Def = OpenedArchetypes.getOpenedArchetypeDef(archetypeTy);
require(Def, "Opened archetype should be registered in SILFunction");
} else if (t->hasDynamicSelfType()) {
require(I->getFunction()->hasSelfParam(),
require(I->getFunction()->hasSelfParam() ||
I->getFunction()->hasSelfMetadataParam(),
"Function containing dynamic self type must have self parameter");
Def = I->getFunction()->getSelfArgument();
if (I->getFunction()->hasSelfMetadataParam())
Def = I->getFunction()->getArguments().back();
else
Def = I->getFunction()->getSelfArgument();
} else {
return;
}