Skip to content

Commit d22b3a7

Browse files
committed
Sema: Move the Optional-typed nil peephole to SILGen
When applying a solution to a nil literal of Optional type, we would build a direct reference to Optional<T>.none instead of leaving the NilLiteralExpr in place, because this would generate more efficient SIL that avoided the call to the Optional(nilLiteral: ()) witness. However, a few places in the type checker build type-checked AST, and they build NilLiteralExpr directly. Moving the peephole to SILGen's lowering of NilLiteralExpr allows us to simplify generated SIL even further by eliding an unnecessary metatype value. Furthermore, it allows SILGen to accept NilLiteralExprs that do not have a ConcreteDeclRef set, which makes type-checked AST easier to build.
1 parent 0455913 commit d22b3a7

File tree

5 files changed

+38
-47
lines changed

5 files changed

+38
-47
lines changed

lib/SILGen/SILGenExpr.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -931,6 +931,25 @@ RValue RValueEmitter::visitOtherConstructorDeclRefExpr(
931931
}
932932

933933
RValue RValueEmitter::visitNilLiteralExpr(NilLiteralExpr *E, SGFContext C) {
934+
// Peephole away the call to Optional<T>(nilLiteral: ()).
935+
if (E->getType()->getOptionalObjectType()) {
936+
auto *noneDecl = SGF.getASTContext().getOptionalNoneDecl();
937+
auto enumTy = SGF.getLoweredType(E->getType());
938+
939+
ManagedValue noneValue;
940+
if (enumTy.isLoadable(SGF.F) || !SGF.silConv.useLoweredAddresses()) {
941+
noneValue = ManagedValue::forUnmanaged(
942+
SGF.B.createEnum(E, SILValue(), noneDecl, enumTy));
943+
} else {
944+
noneValue =
945+
SGF.B.bufferForExpr(E, enumTy, SGF.getTypeLowering(enumTy), C,
946+
[&](SILValue newAddr) {
947+
SGF.B.createInjectEnumAddr(E, newAddr, noneDecl);
948+
});
949+
}
950+
return RValue(SGF, E, noneValue);
951+
}
952+
934953
return SGF.emitLiteral(E, C);
935954
}
936955

lib/Sema/CSApply.cpp

Lines changed: 10 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1912,57 +1912,27 @@ namespace {
19121912
}
19131913

19141914
Expr *visitNilLiteralExpr(NilLiteralExpr *expr) {
1915+
auto type = simplifyType(cs.getType(expr));
1916+
1917+
// By far the most common 'nil' literal is for Optional<T>.none.
1918+
// We don't have to look up the witness in this case since SILGen
1919+
// knows how to lower it directly.
1920+
if (auto objectType = type->getOptionalObjectType()) {
1921+
cs.setType(expr, type);
1922+
return expr;
1923+
}
1924+
19151925
auto &tc = cs.getTypeChecker();
19161926
auto *protocol = tc.getProtocol(expr->getLoc(),
19171927
KnownProtocolKind::ExpressibleByNilLiteral);
19181928

19191929
// For type-sugar reasons, prefer the spelling of the default literal
19201930
// type.
1921-
auto type = simplifyType(cs.getType(expr));
19221931
if (auto defaultType = tc.getDefaultType(protocol, dc)) {
19231932
if (defaultType->isEqual(type))
19241933
type = defaultType;
19251934
}
19261935

1927-
// By far the most common 'nil' literal is for Optional<T>.none.
1928-
//
1929-
// Emit this case directly instead of calling Optional.init(nilLiteral:),
1930-
// since this generates more efficient SIL.
1931-
if (auto objectType = type->getOptionalObjectType()) {
1932-
auto *nilDecl = tc.Context.getOptionalNoneDecl();
1933-
tc.validateDecl(nilDecl);
1934-
if (!nilDecl->hasInterfaceType())
1935-
return nullptr;
1936-
1937-
auto genericSig =
1938-
nilDecl->getDeclContext()->getGenericSignatureOfContext();
1939-
SubstitutionMap subs =
1940-
SubstitutionMap::get(genericSig, llvm::makeArrayRef(objectType),
1941-
{ });
1942-
ConcreteDeclRef concreteDeclRef(nilDecl, subs);
1943-
1944-
auto nilType = FunctionType::get(
1945-
{FunctionType::Param(MetatypeType::get(type))}, type);
1946-
auto *nilRefExpr = new (tc.Context) DeclRefExpr(
1947-
concreteDeclRef, DeclNameLoc(expr->getLoc()),
1948-
/*implicit=*/true, AccessSemantics::Ordinary,
1949-
nilType);
1950-
cs.cacheType(nilRefExpr);
1951-
1952-
auto *typeExpr = TypeExpr::createImplicitHack(
1953-
expr->getLoc(),
1954-
type,
1955-
tc.Context);
1956-
cs.cacheType(typeExpr);
1957-
1958-
auto *callExpr = new (tc.Context) DotSyntaxCallExpr(
1959-
nilRefExpr, expr->getLoc(), typeExpr, type);
1960-
callExpr->setImplicit(true);
1961-
cs.cacheType(callExpr);
1962-
1963-
return callExpr;
1964-
}
1965-
19661936
DeclName initName(tc.Context, DeclBaseName::createConstructor(),
19671937
{ tc.Context.Id_nilLiteral });
19681938
return convertLiteralInPlace(expr, type, protocol,

lib/Sema/TypeCheckError.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -685,8 +685,13 @@ class ApplyClassifier {
685685
PotentialReason::forDefaultArgument());
686686
}
687687

688-
// If this argument is `nil` literal or `.none`,
689-
// it doesn't cause the call to throw.
688+
// If this argument is `nil` literal, it doesn't cause the call to throw.
689+
if (isa<NilLiteralExpr>(arg)) {
690+
if (arg->getType()->getOptionalObjectType())
691+
return Classification();
692+
}
693+
694+
// Neither does 'Optional<T>.none'.
690695
if (auto *DSCE = dyn_cast<DotSyntaxCallExpr>(arg)) {
691696
if (auto *DE = dyn_cast<DeclRefExpr>(DSCE->getFn())) {
692697
auto &ctx = paramType->getASTContext();

test/SILGen/extensions.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ func extensionMethodCurrying(_ x: Foo) {
4444

4545
// CHECK-LABEL: sil hidden [transparent] [ossa] @$s10extensions3BoxV1txSgvpfi : $@convention(thin) <T> () -> @out Optional<T>
4646
// CHECK: bb0(%0 : $*Optional<T>):
47-
// CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thin Optional<T>.Type
4847
// CHECK: inject_enum_addr %0 : $*Optional<T>, #Optional.none!enumelt
4948
// CHECK-NEXT: [[RESULT:%.*]] = tuple ()
5049
// CHECK-NEXT: return [[RESULT]] : $()

test/SILGen/stored_property_default_arg.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,13 @@ struct J {
7676

7777
// CHECK-LABEL: sil hidden [ossa] @$s27stored_property_default_arg16checkOptionalNilyyF : $@convention(thin) () -> () {
7878
func checkOptionalNil() {
79-
// CHECK: {{.*}} = metatype $@thin Optional<Int>.Type
80-
// CHECK-NEXT: [[L1_REF:%.*]] = enum $Optional<Int>, #Optional.none!enumelt
79+
// CHECK: [[L1_REF:%.*]] = enum $Optional<Int>, #Optional.none!enumelt
8180
// CHECK-NEXT: function_ref J.init(k:l:)
8281
// CHECK-NEXT: [[J1_REF:%.*]] = function_ref @$s27stored_property_default_arg1JV1k1lACSbSg_SiSgtcfC : $@convention(method) (Optional<Bool>, Optional<Int>, @thin J.Type) -> J
8382
// CHECK-NEXT: {{.*}} = apply [[J1_REF]]({{.*}}, [[L1_REF]], {{.*}}) : $@convention(method) (Optional<Bool>, Optional<Int>, @thin J.Type) -> J
8483
let m = J(k: true)
8584

86-
// CHECK: {{.*}} = metatype $@thin Optional<Bool>.Type
87-
// CHECK-NEXT: [[K1_REF:%.*]] = enum $Optional<Bool>, #Optional.none!enumelt
85+
// CHECK: [[K1_REF:%.*]] = enum $Optional<Bool>, #Optional.none!enumelt
8886
// CHECK: function_ref J.init(k:l:)
8987
// CHECK-NEXT: [[J2_REF:%.*]] = function_ref @$s27stored_property_default_arg1JV1k1lACSbSg_SiSgtcfC : $@convention(method) (Optional<Bool>, Optional<Int>, @thin J.Type) -> J
9088
// CHECK-NEXT: {{.*}} = apply [[J2_REF]]([[K1_REF]], {{.*}}, {{.*}}) : $@convention(method) (Optional<Bool>, Optional<Int>, @thin J.Type) -> J

0 commit comments

Comments
 (0)