Skip to content

Commit 9e645cc

Browse files
committed
GenericSpecializer: drop metatype arguments in specialized functions
And replace them with explicit `metatype` instruction in the entry block. This allows such metatype instructions to be deleted if they are dead. This was already done for performance-annotated functions. But now do this for all functions. It is essential that performance-annotated functions are specialized in the same way as other functions. Because otherwise it can happen that the same specialization has different performance characteristics in different modules. And it's up to the linker to select one of those ODR functions when linking. Also, dropping metatype arguments is good for performance and code size in general. This change also contains a few bug fixes for dropping metatype arguments. rdar://110509780
1 parent 74822bc commit 9e645cc

13 files changed

+223
-74
lines changed

include/swift/SILOptimizer/Utils/Generics.h

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,6 @@ class ReabstractionInfo {
157157
LoadableAndTrivial
158158
};
159159

160-
unsigned param2ArgIndex(unsigned ParamIdx) const {
161-
return ParamIdx + NumFormalIndirectResults;
162-
}
163-
164160
// Create a new substituted type with the updated signature.
165161
CanSILFunctionType createSubstitutedType(SILFunction *OrigF,
166162
SubstitutionMap SubstMap,
@@ -199,8 +195,8 @@ class ReabstractionInfo {
199195
ApplySite Apply, SILFunction *Callee,
200196
SubstitutionMap ParamSubs,
201197
IsSerialized_t Serialized,
202-
bool ConvertIndirectToDirect = true,
203-
bool dropMetatypeArgs = false,
198+
bool ConvertIndirectToDirect,
199+
bool dropMetatypeArgs,
204200
OptRemark::Emitter *ORE = nullptr);
205201

206202
/// Constructs the ReabstractionInfo for generic function \p Callee with
@@ -214,7 +210,11 @@ class ReabstractionInfo {
214210
IsSerialized_t isSerialized() const {
215211
return Serialized;
216212
}
217-
213+
214+
unsigned param2ArgIndex(unsigned ParamIdx) const {
215+
return ParamIdx + NumFormalIndirectResults;
216+
}
217+
218218
/// Returns true if the specialized function needs an alternative mangling.
219219
/// See hasConvertedResilientParams.
220220
bool needAlternativeMangling() const {
@@ -314,6 +314,8 @@ class ReabstractionInfo {
314314
CanSILFunctionType createSpecializedType(CanSILFunctionType SubstFTy,
315315
SILModule &M) const;
316316

317+
CanSILFunctionType createThunkType(PartialApplyInst *forPAI) const;
318+
317319
SILFunction *getNonSpecializedFunction() const { return Callee; }
318320

319321
/// Map type into a context of the specialized function.

lib/SILOptimizer/IPO/CapturePropagation.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ static SILFunction *getSpecializedWithDeadParams(
492492
FuncBuilder.getModule().getSwiftModule(),
493493
FuncBuilder.getModule().isWholeModule(), ApplySite(), Specialized,
494494
PAI->getSubstitutionMap(), Specialized->isSerialized(),
495-
/* ConvertIndirectToDirect */ false);
495+
/* ConvertIndirectToDirect */ false, /*dropMetatypeArgs=*/ false);
496496
GenericFuncSpecializer FuncSpecializer(FuncBuilder,
497497
Specialized,
498498
ReInfo.getClonerParamSubstitutionMap(),

lib/SILOptimizer/IPO/UsePrespecialized.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,9 @@ bool UsePrespecialized::replaceByPrespecialized(SILFunction &F) {
9191
continue;
9292

9393
ReabstractionInfo ReInfo(M.getSwiftModule(), M.isWholeModule(), AI,
94-
ReferencedF, Subs, IsNotSerialized);
94+
ReferencedF, Subs, IsNotSerialized,
95+
/*ConvertIndirectToDirect=*/ true,
96+
/*dropMetatypeArgs=*/ false);
9597

9698
if (!ReInfo.canBeSpecialized())
9799
continue;

lib/SILOptimizer/Utils/Generics.cpp

Lines changed: 111 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -997,6 +997,35 @@ ReabstractionInfo::createSubstitutedType(SILFunction *OrigF,
997997
return NewFnTy;
998998
}
999999

1000+
CanSILFunctionType ReabstractionInfo::createThunkType(PartialApplyInst *forPAI) const {
1001+
if (!hasDroppedMetatypeArgs())
1002+
return SubstitutedType;
1003+
1004+
llvm::SmallVector<SILParameterInfo, 8> newParams;
1005+
auto params = SubstitutedType->getParameters();
1006+
unsigned firstAppliedParamIdx = params.size() - forPAI->getArguments().size();
1007+
1008+
for (unsigned paramIdx = 0; paramIdx < params.size(); ++paramIdx) {
1009+
if (paramIdx >= firstAppliedParamIdx && isDroppedMetatypeArg(param2ArgIndex(paramIdx)))
1010+
continue;
1011+
newParams.push_back(params[paramIdx]);
1012+
}
1013+
1014+
auto newFnTy = SILFunctionType::get(
1015+
SubstitutedType->getInvocationGenericSignature(),
1016+
SubstitutedType->getExtInfo(), SubstitutedType->getCoroutineKind(),
1017+
SubstitutedType->getCalleeConvention(), newParams,
1018+
SubstitutedType->getYields(), SubstitutedType->getResults(),
1019+
SubstitutedType->getOptionalErrorResult(),
1020+
SubstitutedType->getPatternSubstitutions(), SubstitutionMap(),
1021+
SubstitutedType->getASTContext(),
1022+
SubstitutedType->getWitnessMethodConformanceOrInvalid());
1023+
1024+
// This is an interface type. It should not have any archetypes.
1025+
assert(!newFnTy->hasArchetype());
1026+
return newFnTy;
1027+
}
1028+
10001029
/// Convert the substituted function type into a specialized function type based
10011030
/// on the ReabstractionInfo.
10021031
CanSILFunctionType ReabstractionInfo::
@@ -1067,8 +1096,10 @@ createSpecializedType(CanSILFunctionType SubstFTy, SILModule &M) const {
10671096
: CanGenericSignature();
10681097

10691098
SILFunctionType::ExtInfo extInfo = SubstFTy->getExtInfo();
1099+
ProtocolConformanceRef conf = SubstFTy->getWitnessMethodConformanceOrInvalid();
10701100
if (extInfo.hasSelfParam() && removedSelfParam) {
10711101
extInfo = extInfo.withRepresentation(SILFunctionTypeRepresentation::Thin);
1102+
conf = ProtocolConformanceRef();
10721103
assert(!extInfo.hasSelfParam());
10731104
}
10741105

@@ -1077,7 +1108,7 @@ createSpecializedType(CanSILFunctionType SubstFTy, SILModule &M) const {
10771108
SubstFTy->getCoroutineKind(), SubstFTy->getCalleeConvention(),
10781109
SpecializedParams, SpecializedYields, SpecializedResults,
10791110
SubstFTy->getOptionalErrorResult(), SubstitutionMap(), SubstitutionMap(),
1080-
M.getASTContext(), SubstFTy->getWitnessMethodConformanceOrInvalid());
1111+
M.getASTContext(), conf);
10811112
}
10821113

10831114
/// Create a new generic signature from an existing one by adding
@@ -2546,14 +2577,16 @@ class ReabstractionThunkGenerator {
25462577
protected:
25472578
FullApplySite createReabstractionThunkApply(SILBuilder &Builder);
25482579
SILArgument *convertReabstractionThunkArguments(
2549-
SILBuilder &Builder, SmallVectorImpl<unsigned> &ArgsNeedingEndBorrows);
2580+
SILBuilder &Builder, SmallVectorImpl<unsigned> &ArgsNeedingEndBorrows,
2581+
CanSILFunctionType thunkType);
25502582
};
25512583

25522584
} // anonymous namespace
25532585

25542586
SILFunction *ReabstractionThunkGenerator::createThunk() {
2587+
CanSILFunctionType thunkType = ReInfo.createThunkType(OrigPAI);
25552588
SILFunction *Thunk = FunctionBuilder.getOrCreateSharedFunction(
2556-
Loc, ThunkName, ReInfo.getSubstitutedType(), IsBare, IsTransparent,
2589+
Loc, ThunkName, thunkType, IsBare, IsTransparent,
25572590
ReInfo.isSerialized(), ProfileCounter(), IsThunk, IsNotDynamic,
25582591
IsNotDistributed, IsNotRuntimeAccessible);
25592592
// Re-use an existing thunk.
@@ -2594,7 +2627,7 @@ SILFunction *ReabstractionThunkGenerator::createThunk() {
25942627
// Handle lowered addresses.
25952628
SmallVector<unsigned, 4> ArgsThatNeedEndBorrow;
25962629
SILArgument *ReturnValueAddr =
2597-
convertReabstractionThunkArguments(Builder, ArgsThatNeedEndBorrow);
2630+
convertReabstractionThunkArguments(Builder, ArgsThatNeedEndBorrow, thunkType);
25982631

25992632
FullApplySite ApplySite = createReabstractionThunkApply(Builder);
26002633

@@ -2650,50 +2683,50 @@ FullApplySite ReabstractionThunkGenerator::createReabstractionThunkApply(
26502683
return FullApplySite(TAI);
26512684
}
26522685

2686+
static SILFunctionArgument *addFunctionArgument(SILFunction *function,
2687+
SILType argType,
2688+
SILArgument *copyAttributesFrom) {
2689+
SILBasicBlock *entryBB = function->getEntryBlock();
2690+
auto *src = cast<SILFunctionArgument>(copyAttributesFrom);
2691+
auto *arg = entryBB->createFunctionArgument(argType, src->getDecl());
2692+
arg->setNoImplicitCopy(src->isNoImplicitCopy());
2693+
arg->setLifetimeAnnotation(src->getLifetimeAnnotation());
2694+
arg->setClosureCapture(src->isClosureCapture());
2695+
return arg;
2696+
}
2697+
26532698
/// Create SIL arguments for a reabstraction thunk with lowered addresses. This
26542699
/// may involve replacing indirect arguments with loads and stores. Return the
26552700
/// SILArgument for the address of an indirect result, or nullptr.
26562701
///
26572702
/// FIXME: Remove this if we don't need to create reabstraction thunks after
26582703
/// address lowering.
26592704
SILArgument *ReabstractionThunkGenerator::convertReabstractionThunkArguments(
2660-
SILBuilder &Builder, SmallVectorImpl<unsigned> &ArgsThatNeedEndBorrow) {
2705+
SILBuilder &Builder, SmallVectorImpl<unsigned> &ArgsThatNeedEndBorrow,
2706+
CanSILFunctionType thunkType
2707+
) {
26612708
SILFunction *Thunk = &Builder.getFunction();
26622709
CanSILFunctionType SpecType = SpecializedFunc->getLoweredFunctionType();
2663-
CanSILFunctionType SubstType = ReInfo.getSubstitutedType();
26642710
auto specConv = SpecializedFunc->getConventions();
26652711
(void)specConv;
2666-
SILFunctionConventions substConv(SubstType, M);
2712+
SILFunctionConventions substConv(thunkType, M);
26672713

26682714
assert(specConv.useLoweredAddresses());
26692715

26702716
// ReInfo.NumIndirectResults corresponds to SubstTy's formal indirect
26712717
// results. SpecTy may have fewer formal indirect results.
2672-
assert(SubstType->getNumIndirectFormalResults()
2718+
assert(thunkType->getNumIndirectFormalResults()
26732719
>= SpecType->getNumIndirectFormalResults());
26742720

2675-
SILBasicBlock *EntryBB = Thunk->getEntryBlock();
26762721
SILArgument *ReturnValueAddr = nullptr;
26772722
auto SpecArgIter = SpecializedFunc->getArguments().begin();
2678-
auto cloneSpecializedArgument = [&]() {
2679-
// No change to the argument.
2680-
SILArgument *SpecArg = *SpecArgIter++;
2681-
auto *NewArg =
2682-
EntryBB->createFunctionArgument(SpecArg->getType(), SpecArg->getDecl());
2683-
NewArg->setNoImplicitCopy(
2684-
cast<SILFunctionArgument>(SpecArg)->isNoImplicitCopy());
2685-
NewArg->setLifetimeAnnotation(
2686-
cast<SILFunctionArgument>(SpecArg)->getLifetimeAnnotation());
2687-
NewArg->setClosureCapture(
2688-
cast<SILFunctionArgument>(SpecArg)->isClosureCapture());
2689-
Arguments.push_back(NewArg);
2690-
};
2723+
26912724
// ReInfo.NumIndirectResults corresponds to SubstTy's formal indirect
26922725
// results. SpecTy may have fewer formal indirect results.
2693-
assert(SubstType->getNumIndirectFormalResults()
2726+
assert(thunkType->getNumIndirectFormalResults()
26942727
>= SpecType->getNumIndirectFormalResults());
26952728
unsigned resultIdx = 0;
2696-
for (auto substRI : SubstType->getIndirectFormalResults()) {
2729+
for (auto substRI : thunkType->getIndirectFormalResults()) {
26972730
if (ReInfo.isFormalResultConverted(resultIdx++)) {
26982731
// Convert an originally indirect to direct specialized result.
26992732
// Store the result later.
@@ -2703,36 +2736,42 @@ SILArgument *ReabstractionThunkGenerator::convertReabstractionThunkArguments(
27032736
substConv.getSILType(substRI, Builder.getTypeExpansionContext()));
27042737
assert(ResultTy.isAddress());
27052738
assert(!ReturnValueAddr);
2706-
ReturnValueAddr = EntryBB->createFunctionArgument(ResultTy);
2739+
ReturnValueAddr = Thunk->getEntryBlock()->createFunctionArgument(ResultTy);
27072740
continue;
27082741
}
27092742
// If the specialized result is already indirect, simply clone the indirect
27102743
// result argument.
2711-
assert((*SpecArgIter)->getType().isAddress());
2712-
cloneSpecializedArgument();
2744+
SILArgument *specArg = *SpecArgIter++;
2745+
assert(specArg->getType().isAddress());
2746+
Arguments.push_back(addFunctionArgument(Thunk, specArg->getType(), specArg));
27132747
}
27142748
assert(SpecArgIter
27152749
== SpecializedFunc->getArgumentsWithoutIndirectResults().begin());
27162750
unsigned numParams = SpecType->getNumParameters();
2717-
assert(numParams == SubstType->getNumParameters());
2751+
assert(numParams == thunkType->getNumParameters());
27182752
for (unsigned paramIdx = 0; paramIdx < numParams; ++paramIdx) {
2753+
if (ReInfo.isDroppedMetatypeArg(paramIdx)) {
2754+
if (paramIdx < ApplySite(OrigPAI).getCalleeArgIndexOfFirstAppliedArg()) {
2755+
// Only if the dropped metatype argument is not applied by the `partial_apply`,
2756+
// we need to add it to the thunk.
2757+
SILType ParamTy = SpecializedFunc->mapTypeIntoContext(
2758+
substConv.getSILType(thunkType->getParameters()[paramIdx],
2759+
Builder.getTypeExpansionContext()));
2760+
SILArgument *origArg = OrigF->getArgumentsWithoutIndirectResults()[paramIdx];
2761+
addFunctionArgument(Thunk, ParamTy, origArg);
2762+
}
2763+
continue;
2764+
}
2765+
SILArgument *specArg = *SpecArgIter++;
27192766
if (ReInfo.isParamConverted(paramIdx)) {
27202767
// Convert an originally indirect to direct specialized parameter.
27212768
assert(!specConv.isSILIndirect(SpecType->getParameters()[paramIdx]));
27222769
// Instead of passing the address, pass the loaded value.
27232770
SILType ParamTy = SpecializedFunc->mapTypeIntoContext(
2724-
substConv.getSILType(SubstType->getParameters()[paramIdx],
2771+
substConv.getSILType(thunkType->getParameters()[paramIdx],
27252772
Builder.getTypeExpansionContext()));
27262773
assert(ParamTy.isAddress());
2727-
SILArgument *SpecArg = *SpecArgIter++;
2728-
SILFunctionArgument *NewArg =
2729-
EntryBB->createFunctionArgument(ParamTy, SpecArg->getDecl());
2730-
NewArg->setNoImplicitCopy(
2731-
cast<SILFunctionArgument>(SpecArg)->isNoImplicitCopy());
2732-
NewArg->setLifetimeAnnotation(
2733-
cast<SILFunctionArgument>(SpecArg)->getLifetimeAnnotation());
2734-
NewArg->setClosureCapture(
2735-
cast<SILFunctionArgument>(SpecArg)->isClosureCapture());
2774+
SILFunctionArgument *NewArg = addFunctionArgument(Thunk, ParamTy, specArg);
27362775
if (!NewArg->getArgumentConvention().isGuaranteedConvention()) {
27372776
SILValue argVal = Builder.emitLoadValueOperation(
27382777
Loc, NewArg, LoadOwnershipQualifier::Take);
@@ -2746,7 +2785,7 @@ SILArgument *ReabstractionThunkGenerator::convertReabstractionThunkArguments(
27462785
continue;
27472786
}
27482787
// Simply clone unconverted direct or indirect parameters.
2749-
cloneSpecializedArgument();
2788+
Arguments.push_back(addFunctionArgument(Thunk, specArg->getType(), specArg));
27502789
}
27512790
assert(SpecArgIter == SpecializedFunc->getArguments().end());
27522791
return ReturnValueAddr;
@@ -2773,7 +2812,7 @@ static bool createPrespecialized(StringRef UnspecializedName,
27732812
ReabstractionInfo ReInfo(M.getSwiftModule(), M.isWholeModule(), ApplySite(),
27742813
UnspecFunc, Apply.getSubstitutionMap(),
27752814
IsNotSerialized,
2776-
/*ConvertIndirectToDirect=*/true);
2815+
/*ConvertIndirectToDirect= */true, /*dropMetatypeArgs=*/ false);
27772816

27782817
if (!ReInfo.canBeSpecialized())
27792818
return false;
@@ -2945,7 +2984,7 @@ bool usePrespecialized(
29452984
funcBuilder.getModule().getSwiftModule(),
29462985
funcBuilder.getModule().isWholeModule(), apply, refF, newSubstMap,
29472986
apply.getFunction()->isSerialized() ? IsSerialized : IsNotSerialized,
2948-
/*ConvertIndirectToDirect=*/true, /*dropMetatypeArgs*/true, nullptr);
2987+
/*ConvertIndirectToDirect=*/ true, /*dropMetatypeArgs=*/ false, nullptr);
29492988

29502989
if (layoutReInfo.getSpecializedType() == reInfo.getSpecializedType()) {
29512990
layoutMatches.push_back(
@@ -3005,6 +3044,28 @@ bool usePrespecialized(
30053044
return false;
30063045
}
30073046

3047+
static bool canDropMetatypeArgs(ApplySite apply, SILFunction *callee) {
3048+
for (const Operand &argOp : apply.getArgumentOperands()) {
3049+
SILValue arg = argOp.get();
3050+
if (auto mt = arg->getType().getAs<MetatypeType>()) {
3051+
if (mt->hasRepresentation() && mt->getRepresentation() == MetatypeRepresentation::Thin)
3052+
continue;
3053+
// If the passed thick metatype value is not a `metatype` instruction
3054+
// we don't know the real metatype at runtime. It's not necessarily the
3055+
// same as the declared metatype. It could e.g. be an upcast of a class
3056+
// metatype.
3057+
if (isa<MetatypeInst>(arg))
3058+
continue;
3059+
// But: if the metatype is not used in the callee we don't have to care
3060+
// what metatype value is passed. We can just remove it.
3061+
if (callee->isDefinition() && onlyHaveDebugUses(callee->getArgument(apply.getCalleeArgIndex(argOp))))
3062+
continue;
3063+
return false;
3064+
}
3065+
}
3066+
return true;
3067+
}
3068+
30083069
void swift::trySpecializeApplyOfGeneric(
30093070
SILOptFunctionBuilder &FuncBuilder,
30103071
ApplySite Apply, DeadInstructionSet &DeadApplies,
@@ -3053,7 +3114,7 @@ void swift::trySpecializeApplyOfGeneric(
30533114
FuncBuilder.getModule().isWholeModule(), Apply, RefF,
30543115
Apply.getSubstitutionMap(), Serialized,
30553116
/*ConvertIndirectToDirect=*/ true,
3056-
/*dropMetatypeArgs=*/ isMandatory,
3117+
/*dropMetatypeArgs=*/ canDropMetatypeArgs(Apply, RefF),
30573118
&ORE);
30583119
if (!ReInfo.canBeSpecialized())
30593120
return;
@@ -3210,16 +3271,19 @@ void swift::trySpecializeApplyOfGeneric(
32103271
auto *FRI = Builder.createFunctionRef(PAI->getLoc(), Thunk);
32113272
SmallVector<SILValue, 4> Arguments;
32123273
for (auto &Op : PAI->getArgumentOperands()) {
3274+
unsigned calleeArgIdx = ApplySite(PAI).getCalleeArgIndex(Op);
3275+
if (ReInfo.isDroppedMetatypeArg(calleeArgIdx))
3276+
continue;
32133277
Arguments.push_back(Op.get());
32143278
}
32153279
auto Subs = ReInfo.getCallerParamSubstitutionMap();
32163280
auto FnTy = Thunk->getLoweredFunctionType();
32173281
Subs = SubstitutionMap::get(FnTy->getSubstGenericSignature(), Subs);
3218-
auto *NewPAI = Builder.createPartialApply(
3219-
PAI->getLoc(), FRI, Subs, Arguments,
3220-
PAI->getType().getAs<SILFunctionType>()->getCalleeConvention(),
3221-
PAI->isOnStack());
3222-
PAI->replaceAllUsesWith(NewPAI);
3282+
SingleValueInstruction *newPAI = Builder.createPartialApply(
3283+
PAI->getLoc(), FRI, Subs, Arguments,
3284+
PAI->getType().getAs<SILFunctionType>()->getCalleeConvention(),
3285+
PAI->isOnStack());
3286+
PAI->replaceAllUsesWith(newPAI);
32233287
DeadApplies.insert(PAI);
32243288
return;
32253289
}

test/SIL/Serialization/specializer_can_deserialize.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import Swift
88

99
// CHECK-LABEL: sil {{.*}}@main
1010
// CHECK: bb0({{.*}}):
11-
// CHECK: function_ref @$ss9ContainerVAByxGycfCBi32__Tg5{{.*}}
11+
// CHECK: function_ref @$ss9ContainerVAByxGycfCBi32__Tgm5{{.*}}
1212
// CHECK: function_ref @$ss9ContainerV11doSomethingyyFBi32__Tg5{{.*}}
1313

14-
// CHECK-LABEL: sil shared [noinline] @$ss9ContainerVAByxGycfCBi32__Tg5Tf4d_n
14+
// CHECK-LABEL: sil shared [noinline] @$ss9ContainerVAByxGycfCBi32__Tgm5
1515

1616
// CHECK-LABEL: sil shared [noinline] @$ss9ContainerV11doSomethingyyFBi32__Tg5Tf4d_n
1717

test/SILOptimizer/existential_metatype.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ protocol SomeP {}
33

44
public enum SpecialEnum : SomeP {}
55

6-
// CHECK-LABEL: sil shared [noinline] @$s20existential_metatype17checkProtocolType0aE0Sbxm_tAA5SomePRzlFAA11SpecialEnumO_Tg5Tf4d_n : $@convention(thin) () -> Bool {
6+
// CHECK-LABEL: sil shared [noinline] @$s20existential_metatype17checkProtocolType0aE0Sbxm_tAA5SomePRzlFAA11SpecialEnumO_Tgm5 : $@convention(thin) () -> Bool {
77
// CHECK: bb0:
88
// CHECK-NEXT: %0 = integer_literal $Builtin.Int1, -1
99
// CHECK-NEXT: %1 = struct $Bool (%0 : $Builtin.Int1)
1010
// CHECK-NEXT: return %1 : $Bool
11-
// CHECK-LABEL: } // end sil function '$s20existential_metatype17checkProtocolType0aE0Sbxm_tAA5SomePRzlFAA11SpecialEnumO_Tg5Tf4d_n'
11+
// CHECK-LABEL: } // end sil function '$s20existential_metatype17checkProtocolType0aE0Sbxm_tAA5SomePRzlFAA11SpecialEnumO_Tgm5'
1212
@inline(never)
1313
func checkProtocolType<P : SomeP>(existentialType: P.Type) -> Bool {
1414
return existentialType == SpecialEnum.self

0 commit comments

Comments
 (0)