Skip to content

Commit 944d8d0

Browse files
committed
[SE-0155] Default Arguments in Enum Cases
The bulk of the changes are to SILGenApply. As we must now evaluate the payload ArgumentSource to an RValue, we follow the example of subscripts and lie to the argument emitter. This evaluates arguments at +1 which can lead to slightly worse codegen at -Onone.
1 parent 498b722 commit 944d8d0

15 files changed

+222
-69
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -823,8 +823,6 @@ ERROR(no_default_arg_subscript,none,
823823
"default arguments are not allowed in subscripts", ())
824824
ERROR(no_default_arg_curried,none,
825825
"default arguments are not allowed in curried parameter lists", ())
826-
ERROR(no_default_arg_enum_elt,none,
827-
"default arguments are not allowed in enum cases", ())
828826
ERROR(var_pattern_in_var,none,
829827
"'%select{var|let}0' cannot appear nested inside another 'var' or "
830828
"'let' pattern", (unsigned))

lib/AST/DeclContext.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,9 @@ ResilienceExpansion DeclContext::getResilienceExpansion() const {
314314
// Default argument initializer contexts have their resilience expansion
315315
// set when they're type checked.
316316
if (isa<DefaultArgumentInitializer>(dc)) {
317+
if (auto *EED = dyn_cast<EnumElementDecl>(dc->getParent())) {
318+
return EED->getDefaultArgumentResilienceExpansion();
319+
}
317320
return cast<AbstractFunctionDecl>(dc->getParent())
318321
->getDefaultArgumentResilienceExpansion();
319322
}

lib/Parse/ParseDecl.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5823,13 +5823,14 @@ Parser::parseDeclEnumCase(ParseDeclOptions Flags,
58235823
// See if there's a following argument type.
58245824
ParserResult<ParameterList> ArgParams;
58255825
SmallVector<Identifier, 4> argumentNames;
5826+
DefaultArgumentInfo DefaultArgs;
58265827
if (Tok.isFollowingLParen()) {
58275828
ArgParams = parseSingleParameterClause(ParameterContextKind::EnumElement,
5828-
&argumentNames);
5829+
&argumentNames, &DefaultArgs);
58295830
if (ArgParams.isNull() || ArgParams.hasCodeCompletion())
58305831
return ParserStatus(ArgParams);
58315832
}
5832-
5833+
58335834
// See if there's a raw value expression.
58345835
SourceLoc EqualsLoc;
58355836
ParserResult<Expr> RawValueExpr;
@@ -5896,6 +5897,8 @@ Parser::parseDeclEnumCase(ParseDeclOptions Flags,
58965897
LiteralRawValueExpr,
58975898
CurDeclContext);
58985899

5900+
DefaultArgs.setFunctionContext(result, result->getParameterList());
5901+
58995902
if (NameLoc == CaseLoc) {
59005903
result->setImplicit(); // Parse error
59015904
}

lib/Parse/ParsePattern.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ static ParserStatus parseDefaultArgument(
9191
case Parser::ParameterContextKind::Function:
9292
case Parser::ParameterContextKind::Operator:
9393
case Parser::ParameterContextKind::Initializer:
94+
case Parser::ParameterContextKind::EnumElement:
9495
break;
9596
case Parser::ParameterContextKind::Closure:
9697
diagID = diag::no_default_arg_closure;
@@ -101,9 +102,6 @@ static ParserStatus parseDefaultArgument(
101102
case Parser::ParameterContextKind::Curried:
102103
diagID = diag::no_default_arg_curried;
103104
break;
104-
case Parser::ParameterContextKind::EnumElement:
105-
diagID = diag::no_default_arg_enum_elt;
106-
break;
107105
}
108106

109107
assert((diagID.ID != DiagID()) == !defaultArgs &&

lib/SIL/SILDeclRef.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -467,8 +467,14 @@ IsSerialized_t SILDeclRef::isSerialized() const {
467467
// Default argument generators are serialized if the function was
468468
// type-checked in Swift 4 mode.
469469
if (isDefaultArgGenerator()) {
470-
auto *afd = cast<AbstractFunctionDecl>(d);
471-
switch (afd->getDefaultArgumentResilienceExpansion()) {
470+
ResilienceExpansion expansion;
471+
if (auto *EED = dyn_cast<EnumElementDecl>(d)) {
472+
expansion = EED->getDefaultArgumentResilienceExpansion();
473+
} else {
474+
expansion = cast<AbstractFunctionDecl>(d)
475+
->getDefaultArgumentResilienceExpansion();
476+
}
477+
switch (expansion) {
472478
case ResilienceExpansion::Minimal:
473479
return IsSerialized;
474480
case ResilienceExpansion::Maximal:
@@ -770,7 +776,7 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const {
770776
case SILDeclRef::Kind::DefaultArgGenerator:
771777
assert(!isCurried);
772778
return mangler.mangleDefaultArgumentEntity(
773-
cast<AbstractFunctionDecl>(getDecl()),
779+
cast<DeclContext>(getDecl()),
774780
defaultArgIndex,
775781
SKind);
776782

lib/SILGen/SILGenApply.cpp

Lines changed: 61 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,7 @@ class Callee {
588588
case Kind::IndirectValue:
589589
assert(Substitutions.empty());
590590
return IndirectValue;
591-
591+
case Kind::EnumElement:
592592
case Kind::StandaloneFunction: {
593593
auto constantInfo = SGF.getConstantInfo(*constant);
594594
SILValue ref = SGF.emitGlobalFunctionRef(Loc, *constant, constantInfo);
@@ -600,8 +600,6 @@ class Callee {
600600
SGF.emitGlobalFunctionRef(Loc, *constant, constantInfo, true);
601601
return ManagedValue::forUnmanaged(ref);
602602
}
603-
case Kind::EnumElement:
604-
llvm_unreachable("Should have been curried");
605603
case Kind::ClassMethod: {
606604
auto methodTy = SGF.SGM.Types.getConstantOverrideType(*constant);
607605

@@ -704,8 +702,11 @@ class Callee {
704702
auto constantInfo = SGF.getConstantInfo(*constant);
705703
return createCalleeTypeInfo(SGF, constant, constantInfo.getSILType());
706704
}
707-
case Kind::EnumElement:
708-
llvm_unreachable("Should have been curried");
705+
case Kind::EnumElement: {
706+
// Emit a direct call to the element constructor thunk.
707+
auto constantInfo = SGF.getConstantInfo(*constant);
708+
return createCalleeTypeInfo(SGF, constant, constantInfo.getSILType());
709+
}
709710
case Kind::ClassMethod: {
710711
auto constantInfo = SGF.SGM.Types.getConstantOverrideInfo(*constant);
711712
return createCalleeTypeInfo(SGF, constant, constantInfo.getSILType());
@@ -4526,18 +4527,23 @@ CallEmission::applyEnumElementConstructor(SGFContext C) {
45264527
ArgumentSource payload;
45274528
if (element->hasAssociatedValues()) {
45284529
assert(uncurriedSites.size() == 2);
4530+
SmallVector<ManagedValue, 4> argVals;
4531+
auto resultFnType = cast<FunctionType>(formalResultType);
4532+
auto arg = SGF.prepareEnumPayload(element, resultFnType,
4533+
std::move(uncurriedSites[1]).forward());
4534+
payload = ArgumentSource(element, std::move(arg));
45294535
formalResultType = firstLevelResult.formalType.getResult();
45304536
origFormalType = origFormalType.getFunctionResultType();
45314537
claimNextParamClause(firstLevelResult.formalType);
4532-
payload = std::move(uncurriedSites[1]).forward();
45334538
} else {
45344539
assert(uncurriedSites.size() == 1);
45354540
}
45364541

45374542
assert(substFnType->getNumResults() == 1);
45384543
(void)substFnType;
45394544
ManagedValue resultMV = SGF.emitInjectEnum(
4540-
uncurriedLoc, std::move(payload), SGF.getLoweredType(formalResultType),
4545+
uncurriedLoc, std::move(payload),
4546+
SGF.getLoweredType(formalResultType),
45414547
element, uncurriedContext);
45424548
firstLevelResult.value =
45434549
RValue(SGF, uncurriedLoc, formalResultType, resultMV);
@@ -5247,7 +5253,7 @@ void SILGenFunction::emitRawYield(SILLocation loc,
52475253
/// unnecessary copies by emitting the payload directly into the enum
52485254
/// payload, or into the box in the case of an indirect payload.
52495255
ManagedValue SILGenFunction::emitInjectEnum(SILLocation loc,
5250-
ArgumentSource payload,
5256+
ArgumentSource &&payload,
52515257
SILType enumTy,
52525258
EnumElementDecl *element,
52535259
SGFContext C) {
@@ -5290,8 +5296,8 @@ ManagedValue SILGenFunction::emitInjectEnum(SILLocation loc,
52905296
CleanupHandle uninitCleanup = enterDeallocBoxCleanup(box);
52915297

52925298
BoxInitialization dest(box, addr, uninitCleanup, initCleanup);
5293-
5294-
std::move(payload).forwardInto(*this, origFormalType, &dest, payloadTL);
5299+
std::move(payload).forwardInto(*this, origFormalType, &dest,
5300+
payloadTL);
52955301

52965302
payloadMV = dest.getManagedBox();
52975303
loweredPayloadType = payloadMV.getType();
@@ -5325,8 +5331,7 @@ ManagedValue SILGenFunction::emitInjectEnum(SILLocation loc,
53255331
} else if (payloadTL.isLoadable()) {
53265332
// The payload of this specific enum case might be loadable
53275333
// even if the overall enum is address-only.
5328-
payloadMV =
5329-
std::move(payload).getAsSingleValue(*this, origFormalType);
5334+
payloadMV = std::move(payload).getAsSingleValue(*this, origFormalType);
53305335
B.emitStoreValueOperation(loc, payloadMV.forward(*this), resultData,
53315336
StoreOwnershipQualifier::Init);
53325337
} else {
@@ -5967,6 +5972,36 @@ static void collectFakeIndexParameters(SILGenModule &SGM,
59675972
convention});
59685973
}
59695974

5975+
static void emitPseudoFunctionArguments(SILGenFunction &SGF,
5976+
CanFunctionType substFnType,
5977+
SmallVectorImpl<ManagedValue> &outVals,
5978+
ArgumentSource &&source) {
5979+
auto substParams = substFnType->getParams();
5980+
5981+
SmallVector<SILParameterInfo, 4> substParamTys;
5982+
for (auto substParam : substParams) {
5983+
auto substParamType = substParam.getParameterType()->getCanonicalType();
5984+
collectFakeIndexParameters(SGF.SGM, substParamType, substParamTys);
5985+
}
5986+
5987+
SmallVector<ManagedValue, 4> argValues;
5988+
SmallVector<DelayedArgument, 2> delayedArgs;
5989+
5990+
ArgEmitter emitter(SGF, SILFunctionTypeRepresentation::Thin,
5991+
/*yield*/ false,
5992+
/*isForCoroutine*/ false, ClaimedParamsRef(substParamTys),
5993+
argValues, delayedArgs,
5994+
/*foreign error*/ None, ImportAsMemberStatus());
5995+
5996+
emitter.emitTopLevel(std::move(source), AbstractionPattern(substFnType));
5997+
5998+
// TODO: do something to preserve LValues in the delayed arguments?
5999+
if (!delayedArgs.empty())
6000+
emitDelayedArguments(SGF, delayedArgs, argValues);
6001+
6002+
outVals.swap(argValues);
6003+
}
6004+
59706005
PreparedArguments
59716006
SILGenFunction::prepareSubscriptIndices(SubscriptDecl *subscript,
59726007
SubstitutionMap subs,
@@ -5992,26 +6027,8 @@ SILGenFunction::prepareSubscriptIndices(SubscriptDecl *subscript,
59926027

59936028
auto substParams = substFnType->getParams();
59946029

5995-
SmallVector<SILParameterInfo, 4> substParamTys;
5996-
for (auto substParam : substParams) {
5997-
auto substParamType = substParam.getParameterType()->getCanonicalType();
5998-
collectFakeIndexParameters(SGM, substParamType, substParamTys);
5999-
}
6000-
60016030
SmallVector<ManagedValue, 4> argValues;
6002-
SmallVector<DelayedArgument, 2> delayedArgs;
6003-
6004-
ArgEmitter emitter(*this, SILFunctionTypeRepresentation::Thin,
6005-
/*yield*/ false,
6006-
/*isForCoroutine*/ false, ClaimedParamsRef(substParamTys),
6007-
argValues, delayedArgs,
6008-
/*foreign error*/ None, ImportAsMemberStatus());
6009-
6010-
emitter.emitTopLevel(indexExpr, AbstractionPattern(substFnType));
6011-
6012-
// TODO: do something to preserve LValues in the delayed arguments?
6013-
if (!delayedArgs.empty())
6014-
emitDelayedArguments(*this, delayedArgs, argValues);
6031+
emitPseudoFunctionArguments(*this, substFnType, argValues, indexExpr);
60156032

60166033
PreparedArguments result(substParams, /*isScalar=*/false);
60176034

@@ -6029,6 +6046,19 @@ SILGenFunction::prepareSubscriptIndices(SubscriptDecl *subscript,
60296046
return result;
60306047
}
60316048

6049+
RValue
6050+
SILGenFunction::prepareEnumPayload(EnumElementDecl *element,
6051+
CanFunctionType substFnType,
6052+
ArgumentSource &&args) {
6053+
SmallVector<ManagedValue, 4> argValues;
6054+
emitPseudoFunctionArguments(*this, substFnType, argValues, std::move(args));
6055+
6056+
auto payloadTy = AnyFunctionType::composeInput(getASTContext(),
6057+
substFnType.getParams(),
6058+
/*canonicalVararg*/ true);
6059+
return RValue(*this, argValues, payloadTy->getCanonicalType());
6060+
}
6061+
60326062
SILDeclRef SILGenModule::getAccessorDeclRef(AccessorDecl *accessor) {
60336063
return SILDeclRef(accessor, SILDeclRef::Kind::Func)
60346064
.asForeign(requiresForeignEntryPoint(accessor));

lib/SILGen/SILGenConstructor.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -411,10 +411,9 @@ void SILGenFunction::emitEnumConstructor(EnumElementDecl *element) {
411411
// Emit the exploded constructor argument.
412412
ArgumentSource payload;
413413
if (element->hasAssociatedValues()) {
414-
RValue arg = emitImplicitValueConstructorArg
415-
(*this, Loc, element->getArgumentInterfaceType()->getCanonicalType(),
416-
element->getDeclContext());
417-
payload = ArgumentSource(Loc, std::move(arg));
414+
auto eltArgTy = element->getArgumentInterfaceType()->getCanonicalType();
415+
RValue arg = emitImplicitValueConstructorArg(*this, Loc, eltArgTy, element);
416+
payload = ArgumentSource(Loc, std::move(arg));
418417
}
419418

420419
// Emit the metatype argument.

lib/SILGen/SILGenFunction.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ DeclName SILGenModule::getMagicFunctionName(DeclContext *dc) {
9797
assert(e->getExtendedNominal() && "extension for nonnominal");
9898
return e->getExtendedNominal()->getName();
9999
}
100+
if (auto EED = dyn_cast<EnumElementDecl>(dc)) {
101+
return EED->getFullName();
102+
}
100103
llvm_unreachable("unexpected #function context");
101104
}
102105

@@ -115,7 +118,7 @@ DeclName SILGenModule::getMagicFunctionName(SILDeclRef ref) {
115118
case SILDeclRef::Kind::GlobalAccessor:
116119
return getMagicFunctionName(cast<VarDecl>(ref.getDecl())->getDeclContext());
117120
case SILDeclRef::Kind::DefaultArgGenerator:
118-
return getMagicFunctionName(cast<AbstractFunctionDecl>(ref.getDecl()));
121+
return getMagicFunctionName(cast<DeclContext>(ref.getDecl()));
119122
case SILDeclRef::Kind::StoredPropertyInitializer:
120123
return getMagicFunctionName(cast<VarDecl>(ref.getDecl())->getDeclContext());
121124
case SILDeclRef::Kind::IVarInitializer:

lib/SILGen/SILGenFunction.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -849,7 +849,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
849849
//===--------------------------------------------------------------------===//
850850

851851
ManagedValue emitInjectEnum(SILLocation loc,
852-
ArgumentSource payload,
852+
ArgumentSource &&payload,
853853
SILType enumTy,
854854
EnumElementDecl *element,
855855
SGFContext C);
@@ -1220,6 +1220,10 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
12201220
AccessStrategy strategy,
12211221
Expr *indices);
12221222

1223+
RValue prepareEnumPayload(EnumElementDecl *element,
1224+
CanFunctionType substFnType,
1225+
ArgumentSource &&indexExpr);
1226+
12231227
ArgumentSource prepareAccessorBaseArg(SILLocation loc, ManagedValue base,
12241228
CanType baseFormalType,
12251229
SILDeclRef accessor);

lib/SILGen/SILGenType.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1006,7 +1006,13 @@ class SILGenType : public TypeMemberVisitor<SILGenType> {
10061006
}
10071007

10081008
void visitEnumCaseDecl(EnumCaseDecl *ecd) {}
1009-
void visitEnumElementDecl(EnumElementDecl *ued) {}
1009+
void visitEnumElementDecl(EnumElementDecl *EED) {
1010+
if (!EED->hasAssociatedValues())
1011+
return;
1012+
1013+
// Emit any default argument generators.
1014+
SGM.emitDefaultArgGenerators(EED, EED->getParameterList());
1015+
}
10101016

10111017
void visitPatternBindingDecl(PatternBindingDecl *pd) {
10121018
// Emit initializers.

test/IRGen/enum_resilience.swift

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,11 +151,23 @@ public func constructResilientEnumPayload(_ s: Size) -> Medium {
151151
// CHECK-NEXT: [[METADATA_ADDR:%.*]] = bitcast %swift.type* [[METADATA]] to i8***
152152
// CHECK-NEXT: [[VWT_ADDR:%.*]] = getelementptr inbounds i8**, i8*** [[METADATA_ADDR]], [[INT]] -1
153153
// CHECK-NEXT: [[VWT:%.*]] = load i8**, i8*** [[VWT_ADDR]]
154+
// CHECK-NEXT: [[VWT_CAST:%.*]] = bitcast i8** [[VWT]] to %swift.vwtable*
155+
156+
// CHECK-NEXT: [[WITNESS_ADDR:%.*]] = getelementptr inbounds %swift.vwtable, %swift.vwtable* [[VWT_CAST]], i32 0, i32 8
157+
// CHECK-NEXT: [[WITNESS_FOR_SIZE:%size]] = load [[INT]], [[INT]]* [[WITNESS_ADDR]]
158+
// CHECK-NEXT: [[ALLOCA:%.*]] = alloca i8, {{.*}} [[WITNESS_FOR_SIZE]], align 16
159+
// CHECK-NEXT: call void @llvm.lifetime.start.p0i8({{(i32|i64)}} -1, i8* [[ALLOCA]])
160+
// CHECK-NEXT: [[ENUM_STORAGE:%.*]] = bitcast i8* [[ALLOCA]] to %swift.opaque*
154161

155162
// CHECK-NEXT: [[WITNESS_ADDR:%.*]] = getelementptr inbounds i8*, i8** [[VWT]], i32 2
156-
// CHECK-NEXT: [[WITNESS:%.*]] = load i8*, i8** [[WITNESS_ADDR]]
157-
// CHECK-NEXT: [[WITNESS_FN:%initializeWithCopy]] = bitcast i8* [[WITNESS]]
158-
// CHECK-NEXT: [[COPY:%.*]] = call %swift.opaque* [[WITNESS_FN]](%swift.opaque* noalias %0, %swift.opaque* noalias %1, %swift.type* [[METADATA]])
163+
// CHECK-NEXT: [[WITNESS:%.*]] = load i8*, i8** [[WITNESS_ADDR]]
164+
// CHECK-NEXT: [[WITNESS_FN:%initializeWithCopy]] = bitcast i8* [[WITNESS]] to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)*
165+
// CHECK-NEXT: call %swift.opaque* [[WITNESS_FN]](%swift.opaque* noalias [[ENUM_STORAGE]], %swift.opaque* noalias %1, %swift.type* [[METADATA]])
166+
167+
// CHECK-NEXT: [[WITNESS_ADDR:%.*]] = getelementptr inbounds i8*, i8** [[VWT]], i32 4
168+
// CHECK-NEXT: [[WITNESS:%.*]] = load i8*, i8** [[WITNESS_ADDR]]
169+
// CHECK-NEXT: [[WITNESS_FN:%initializeWithTake]] = bitcast i8* [[WITNESS]] to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)*
170+
// CHECK-NEXT: call %swift.opaque* [[WITNESS_FN]](%swift.opaque* noalias %0, %swift.opaque* noalias [[ENUM_STORAGE]], %swift.type* [[METADATA]])
159171

160172
// CHECK-NEXT: [[TAG:%.*]] = load i32, i32* @"$s14resilient_enum6MediumO8PostcardyAC0A7_struct4SizeVcACmFWC"
161173
// CHECK-NEXT: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$s14resilient_enum6MediumOMa"([[INT]] 0)
@@ -170,6 +182,8 @@ public func constructResilientEnumPayload(_ s: Size) -> Medium {
170182
// CHECK-NEXT: [[WITNESS:%.*]] = load i8*, i8** [[WITNESS_ADDR]]
171183
// CHECK-NEXT: [[WITNESS_FN:%destructiveInjectEnumTag]] = bitcast i8* [[WITNESS]]
172184
// CHECK-NEXT: call void [[WITNESS_FN]](%swift.opaque* noalias %0, i32 [[TAG]], %swift.type* [[METADATA2]])
185+
// CHECK-NEXT: [[STORAGE_ALLOCA:%.*]] = bitcast %swift.opaque* [[ENUM_STORAGE]] to i8*
186+
// CHECK-NEXT: call void @llvm.lifetime.end.p0i8({{(i32|i64)}} -1, i8* [[STORAGE_ALLOCA]])
173187

174188
// CHECK-NEXT: ret void
175189

0 commit comments

Comments
 (0)