Skip to content

SILGen: Fix crash when a stored property with an initial value has a reabstractable type #34255

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

slavapestov
Copy link
Contributor

@slavapestov slavapestov commented Oct 9, 2020

A constructor can constrain generic parameters more than the
type itself, either because there is a 'where' clause on the
constructor, or because the constructor is defined inside an
extension with a 'where' clause.

In this case, when the constructor calls a stored property
initializer to implicitly initialize a stored property with
an initial value, we must apply substitutions to get the
correct result type for the call.

If the original type of the stored property lowers differently
based on the abstraction pattern, for example if it is a
function type, then emitApply() would by default re-abstract
the result to the most specific abstraction pattern possible.

However, this was wrong because we store the result value into
the stored property, and a stored property's type should
always be lowered with the most generic abstraction pattern.

In practice, this meant if you have a stored property of type
(T) -> (), and an initializer with where T == String for
example, we would call the initializer, to produce a value with
lowered type (@in_guaranteed String) -> (), then thunk it to a
(@guaranteed String) -> (), and try to store the thunk into
the stored property -- which has type (@in_guaranteed String) -> ().

This would either miscompile or trigger an assertion.

To get around this, we want to bypass the orig-to-subst
conversion performed in emitApply(). My chosen solution is
to have emitApply() emit the result into a
ConvertingInitialization set up to perform a subst-to-orig
conversion.

Now that ConvertingInitialization is smart enough to
peephole away matched pairs of orig-to-subst and subst-to-orig
conversions, this always reduces to a no-op, and the
emitApply() call produces and stores a value with the
correct abstraction pattern.

Fixes rdar://problem/67419937.

@slavapestov slavapestov changed the title Reabstract stored property constrained extension mess SILGen: Fix crash when a stored property with an initial value has a reabstractable type Oct 9, 2020
@slavapestov slavapestov requested a review from rjmccall October 9, 2020 21:40
@slavapestov
Copy link
Contributor Author

@swift-ci Please test source compatibility

@slavapestov slavapestov force-pushed the reabstract-stored-property-constrained-extension-mess branch from 4197ef2 to 2ad7f2c Compare October 9, 2020 22:54
@slavapestov
Copy link
Contributor Author

@swift-ci Please smoke test

@slavapestov
Copy link
Contributor Author

@swift-ci Please test source compatibility

@slavapestov slavapestov force-pushed the reabstract-stored-property-constrained-extension-mess branch from 2ad7f2c to 1ec2e7b Compare October 11, 2020 00:05
@slavapestov
Copy link
Contributor Author

@swift-ci Please test source compatibility

@slavapestov
Copy link
Contributor Author

@swift-ci Please benchmark

@slavapestov slavapestov marked this pull request as draft October 11, 2020 00:09
@swift-ci
Copy link
Contributor

Performance: -O

Regression OLD NEW DELTA RATIO
DictionaryKeysContainsNative 26 30 +15.4% 0.87x (?)

Code size: -O

Regression OLD NEW DELTA RATIO
Hash.o 22047 22815 +3.5% 0.97x
LinkedList.o 2231 2263 +1.4% 0.99x
Hanoi.o 3030 3062 +1.1% 0.99x
 
Improvement OLD NEW DELTA RATIO
ObserverForwarderStruct.o 2434 2402 -1.3% 1.01x

Performance: -Osize

Regression OLD NEW DELTA RATIO
FlattenListFlatMap 3918 6215 +58.6% 0.63x (?)
StringBuilderLong 1350 1570 +16.3% 0.86x (?)
StringBuilderWithLongSubstring 1950 2220 +13.8% 0.88x (?)
ProtocolDispatch2 500 564 +12.8% 0.89x
SortAdjacentIntPyramids 1580 1780 +12.7% 0.89x
ProtocolDispatch 342 371 +8.5% 0.92x
ObjectiveCBridgeFromNSSetAnyObjectToStringForced 84500 91000 +7.7% 0.93x (?)

Code size: -Osize

Improvement OLD NEW DELTA RATIO
ObserverForwarderStruct.o 2728 2645 -3.0% 1.03x
ObserverClosure.o 2481 2427 -2.2% 1.02x
ObserverPartiallyAppliedMethod.o 2547 2493 -2.1% 1.02x
OpaqueConsumingUsers.o 2258 2217 -1.8% 1.02x
ObserverUnappliedMethod.o 4998 4922 -1.5% 1.02x
Hanoi.o 2919 2875 -1.5% 1.02x

Performance: -Onone

Regression OLD NEW DELTA RATIO
StringToDataLargeUnicode 9200 10000 +8.7% 0.92x (?)
 
Improvement OLD NEW DELTA RATIO
SevenBoom 2507 2041 -18.6% 1.23x (?)
NSError 1066 946 -11.3% 1.13x (?)

Code size: -swiftlibs

How to read the data The tables contain differences in performance which are larger than 8% and differences in code size which are larger than 1%.

If you see any unexpected regressions, you should consider fixing the
regressions before you merge the PR.

Noise: Sometimes the performance results (not code size!) contain false
alarms. Unexpected regressions which are marked with '(?)' are probably noise.
If you see regressions which you cannot explain you can try to run the
benchmarks again. If regressions still show up, please consult with the
performance team (@eeckstein).

Hardware Overview
  Model Name: Mac Pro
  Model Identifier: MacPro6,1
  Processor Name: 12-Core Intel Xeon E5
  Processor Speed: 2.7 GHz
  Number of Processors: 1
  Total Number of Cores: 12
  L2 Cache (per Core): 256 KB
  L3 Cache: 30 MB
  Memory: 64 GB

Instead of constructing an LValue and storing the result of the
stored property initializer to the lvalue, let's emit the call
of the stored property initializer directly into an initialization
pointing at the stored properties named by the pattern.
…reabstractable type

A constructor can constrain generic parameters more than the
type itself, either because there is a 'where' clause on the
constructor, or because the constructor is defined inside an
extension with a 'where' clause.

In this case, when the constructor calls a stored property
initializer to implicitly initialize a stored property with
an initial value, we must apply substitutions to get the
correct result type for the call.

If the original type of the stored property lowers differently
based on the abstraction pattern, for example if it is a
function type, then emitApply() would by default re-abstract
the result to the most specific abstraction pattern possible.

However, this was wrong because we store the result value into
the stored property, and a stored property's type should
always be lowered with the most generic abstraction pattern.

In practice, this meant if you have a stored property of type
(T) -> (), and an initializer with 'where T == String' for
example, we would call the initializer, to produce a value with
lowered type (@in_guaranteed String) -> (), then thunk it to a
(@guaranteed String) -> (), and try to store the thunk into
the stored property -- which has type (@in_guaranteed String) -> ().

This would either miscompile or trigger an assertion.

To get around this, we want to bypass the orig-to-subst
conversion performed in emitApply(). My chosen solution is
to have emitApply() emit the result into a
ConvertingInitialization set up to perform a subst-to-orig
conversion.

Now that ConvertingInitialization is smart enough to
peephole away matched pairs of orig-to-subst and subst-to-orig
conversions, this always reduces to a no-op, and the
emitApply() call produces and stores a value with the
correct abstraction pattern.

Fixes <rdar://problem/67419937>.
@slavapestov slavapestov force-pushed the reabstract-stored-property-constrained-extension-mess branch from 1ec2e7b to 7fd4615 Compare October 11, 2020 16:49
@slavapestov
Copy link
Contributor Author

@swift-ci Please smoke test

@slavapestov
Copy link
Contributor Author

@swift-ci Please test Windows platform

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants