Skip to content

Commit d1416dd

Browse files
committed
SILGen: Stub unavailable functions.
When `-unavailable-decl-optimization=stub` is specified, insert a call to `_diagnoseUnavailableCodeReached()` at the beginning of the function to cause it to trap if executed at run time. Part of rdar://107388493
1 parent c735272 commit d1416dd

11 files changed

+116
-0
lines changed

include/swift/AST/KnownDecls.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ FUNC_DECL(DiagnoseUnexpectedError, "_unexpectedError")
6868
FUNC_DECL(DiagnoseUnexpectedNilOptional, "_diagnoseUnexpectedNilOptional")
6969
FUNC_DECL(DiagnoseUnexpectedEnumCase, "_diagnoseUnexpectedEnumCase")
7070
FUNC_DECL(DiagnoseUnexpectedEnumCaseValue, "_diagnoseUnexpectedEnumCaseValue")
71+
FUNC_DECL(DiagnoseUnavailableCodeReached, "_diagnoseUnavailableCodeReached")
7172

7273
FUNC_DECL(GetErrorEmbeddedNSError, "_getErrorEmbeddedNSError")
7374

include/swift/SIL/SILModule.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,6 +1083,10 @@ LLVM_LIBRARY_VISIBILITY bool usesObjCAllocator(ClassDecl *theClass);
10831083
/// A declaration may not require lowering if, for example, it is annotated as
10841084
/// unavailable and optimization settings allow it to be omitted.
10851085
LLVM_LIBRARY_VISIBILITY bool shouldSkipLowering(Decl *D);
1086+
1087+
/// Returns true if SIL/IR lowering for the given declaration should produce
1088+
/// a stub that traps at runtime because the code ought to be unreachable.
1089+
LLVM_LIBRARY_VISIBILITY bool shouldLowerToUnavailableCodeStub(Decl *D);
10861090
} // namespace Lowering
10871091

10881092
/// Apply the given function to each ABI member of \c D skipping the members

lib/SIL/IR/SILModule.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -965,3 +965,13 @@ bool Lowering::shouldSkipLowering(Decl *D) {
965965
// -unavailable-decl-optimization=complete is specified.
966966
return D->getSemanticUnavailableAttr() != None;
967967
}
968+
969+
bool Lowering::shouldLowerToUnavailableCodeStub(Decl *D) {
970+
if (D->getASTContext().LangOpts.UnavailableDeclOptimizationMode !=
971+
UnavailableDeclOptimization::Stub)
972+
return false;
973+
974+
// Unavailable declarations should trap at runtime if
975+
// -unavailable-decl-optimization=stub is specified.
976+
return D->getSemanticUnavailableAttr() != None;
977+
}

lib/SILGen/SILGenApply.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5863,6 +5863,23 @@ SILGenFunction::emitApplyOfLibraryIntrinsic(SILLocation loc,
58635863
finalArgs, calleeTypeInfo, ApplyOptions(), ctx, None);
58645864
}
58655865

5866+
void SILGenFunction::emitApplyOfUnavailableCodeReached() {
5867+
auto loc = RegularLocation::getAutoGeneratedLocation(F.getLocation());
5868+
5869+
// FIXME: When the deployment target is < Swift 5.9 the stdlib function will
5870+
// be unavailable. Call the @backDeployed fallback function instead.
5871+
auto *diagnoseUnavailableCodeReachedFunc =
5872+
getASTContext().getDiagnoseUnavailableCodeReached();
5873+
5874+
if (!diagnoseUnavailableCodeReachedFunc) {
5875+
B.createUnconditionalFail(loc, "unavailable code reached");
5876+
return;
5877+
}
5878+
5879+
emitApplyOfLibraryIntrinsic(loc, diagnoseUnavailableCodeReachedFunc,
5880+
SubstitutionMap(), {}, SGFContext());
5881+
}
5882+
58665883
StringRef SILGenFunction::getMagicFunctionString() {
58675884
assert(MagicFunctionName
58685885
&& "asking for #function but we don't have a function name?!");

lib/SILGen/SILGenFunction.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,9 @@ void SILGenFunction::emitFunction(FuncDecl *fd) {
882882
prepareEpilog(fd->getResultInterfaceType(),
883883
fd->hasThrows(), CleanupLocation(fd));
884884

885+
if (shouldLowerToUnavailableCodeStub(fd))
886+
emitApplyOfUnavailableCodeReached();
887+
885888
emitProfilerIncrement(fd->getTypecheckedBody());
886889

887890
// Emit the actual function body as usual

lib/SILGen/SILGenFunction.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1901,6 +1901,10 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
19011901
ArrayRef<ManagedValue> args,
19021902
SGFContext ctx);
19031903

1904+
/// Emits a call to the `_diagnoseUnavailableCodeReached()` function in the
1905+
/// standard library.
1906+
void emitApplyOfUnavailableCodeReached();
1907+
19041908
RValue emitApplyAllocatingInitializer(SILLocation loc, ConcreteDeclRef init,
19051909
PreparedArguments &&args, Type overriddenSelfType,
19061910
SGFContext ctx);

stdlib/public/core/AssertCommon.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,3 +275,18 @@ internal func _diagnoseUnexpectedEnumCase<SwitchedValue>(
275275
"unexpected enum case while switching on value of type '\(type)'",
276276
flags: _fatalErrorFlags())
277277
}
278+
279+
/// Called when a function marked `unavailable` with `@available` is invoked
280+
/// and the module containing the unavailable function was compiled with
281+
/// `-unavailable-decl-optimization=stub`.
282+
///
283+
/// This function should not be inlined because it is cold and inlining just
284+
/// bloats code.
285+
@backDeployed(before: SwiftStdlib 5.9)
286+
@inline(never)
287+
@_semantics("unavailable_code_reached")
288+
@usableFromInline // COMPILER_INTRINSIC
289+
internal func _diagnoseUnavailableCodeReached() -> Never {
290+
_assertionFailure(
291+
"Fatal error", "Unavailable code reached", flags: _fatalErrorFlags())
292+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -module-name main -Xfrontend -unavailable-decl-optimization=stub %s -o %t/a.out
3+
// RUN: %target-codesign %t/a.out
4+
// RUN: %target-run %t/a.out > %t/output 2>&1 || true
5+
// RUN: %FileCheck %s < %t/output
6+
7+
// REQUIRES: executable_test
8+
9+
@available(*, unavailable)
10+
public func foo() {
11+
print("Can't call this")
12+
}
13+
14+
// To bypass the typechecker, forward declare an available function with the
15+
// same mangling as foo.
16+
@_silgen_name("$s4main3fooyyF")
17+
func callFoo()
18+
19+
// CHECK: Fatal error: Unavailable code reached
20+
// CHECK-NOT: Can't call this
21+
callFoo()
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %target-swift-emit-silgen -module-name Test -parse-as-library %s -verify -unavailable-decl-optimization=stub | %FileCheck %s --check-prefixes=CHECK
2+
3+
public struct S {}
4+
5+
// CHECK-LABEL: sil {{.*}} @$s4Test15unavailableFuncAA1SVyF
6+
// CHECK: [[FNREF:%.*]] = function_ref @$ss31_diagnoseUnavailableCodeReacheds5NeverOyF
7+
// CHECK-NEXT: [[APPLY:%.*]] = apply [[FNREF]]()
8+
// CHECK: function_ref @$s4Test1SVACycfC
9+
// CHECK: } // end sil function '$s4Test15unavailableFuncAA1SVyF'
10+
@available(*, unavailable)
11+
public func unavailableFunc() -> S {
12+
return S()
13+
}
14+
15+
enum SomeError: Error { case generic }
16+
17+
// CHECK-LABEL: sil {{.*}} @$s4Test23unavailableThrowingFuncyyKF
18+
// CHECK: [[FNREF:%.*]] = function_ref @$ss31_diagnoseUnavailableCodeReacheds5NeverOyF
19+
// CHECK-NEXT: [[APPLY:%.*]] = apply [[FNREF]]()
20+
// CHECK: throw
21+
// CHECK: } // end sil function '$s4Test23unavailableThrowingFuncyyKF'
22+
@available(*, unavailable)
23+
public func unavailableThrowingFunc() throws {
24+
throw SomeError.generic
25+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %target-swift-emit-silgen -target %target-swift-abi-5.1-triple -module-name Test -parse-as-library %s -verify -unavailable-decl-optimization=stub | %FileCheck %s --check-prefixes=CHECK
2+
3+
public protocol P {}
4+
public struct S {}
5+
extension S: P {}
6+
7+
// CHECK-LABEL: sil {{.*}} @$s4Test27unavailableOpaqueReturnFuncQryF
8+
// CHECK: [[FNREF:%.*]] = function_ref @$ss31_diagnoseUnavailableCodeReacheds5NeverOyF
9+
// CHECK-NEXT: [[APPLY:%.*]] = apply [[FNREF]]()
10+
// CHECK: function_ref @$s4Test1SVACycfC
11+
// CHECK: } // end sil function '$s4Test27unavailableOpaqueReturnFuncQryF'
12+
@available(*, unavailable)
13+
public func unavailableOpaqueReturnFunc() -> some P {
14+
return S()
15+
}

0 commit comments

Comments
 (0)