Skip to content

Commit b299e16

Browse files
authored
Merge pull request #25592 from jrose-apple/5.1-override-the-train
[5.1] Add the notion of @_implementationOnly overrides
2 parents cedcace + 80cce2b commit b299e16

File tree

19 files changed

+582
-14
lines changed

19 files changed

+582
-14
lines changed

include/swift/AST/Attr.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ SIMPLE_DECL_ATTR(_alwaysEmitIntoClient, AlwaysEmitIntoClient,
395395
OnVar | OnSubscript | OnAbstractFunction | UserInaccessible,
396396
83)
397397
SIMPLE_DECL_ATTR(_implementationOnly, ImplementationOnly,
398-
OnImport | UserInaccessible,
398+
OnImport | OnFunc | OnConstructor | OnVar | OnSubscript | UserInaccessible,
399399
84)
400400
DECL_ATTR(_custom, Custom,
401401
OnAnyDecl | UserInaccessible,

include/swift/AST/DiagnosticsSema.def

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2473,6 +2473,18 @@ WARNING(warn_implementation_only_conflict,none,
24732473
NOTE(implementation_only_conflict_here,none,
24742474
"imported as implementation-only here", ())
24752475

2476+
ERROR(implementation_only_decl_non_override,none,
2477+
"'@_implementationOnly' can only be used on overrides", ())
2478+
ERROR(implementation_only_override_changed_type,none,
2479+
"'@_implementationOnly' override must have the same type as the "
2480+
"declaration it overrides (%0)", (Type))
2481+
ERROR(implementation_only_override_without_attr,none,
2482+
"override of '@_implementationOnly' %0 should also be declared "
2483+
"'@_implementationOnly'", (DescriptiveDeclKind))
2484+
ERROR(implementation_only_override_import_without_attr,none,
2485+
"override of %0 imported as implementation-only must be declared "
2486+
"'@_implementationOnly'", (DescriptiveDeclKind))
2487+
24762488
// Derived conformances
24772489
ERROR(cannot_synthesize_init_in_extension_of_nonfinal,none,
24782490
"implementation of %0 for non-final class cannot be automatically "

lib/AST/ASTPrinter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ PrintOptions PrintOptions::printParseableInterfaceFile(bool preferTypeRepr) {
126126

127127
class ShouldPrintForParseableInterface : public ShouldPrintChecker {
128128
bool shouldPrint(const Decl *D, const PrintOptions &options) override {
129+
// Skip anything that is marked `@_implementationOnly` itself.
130+
if (D->getAttrs().hasAttribute<ImplementationOnlyAttr>())
131+
return false;
132+
129133
// Skip anything that isn't 'public' or '@usableFromInline'.
130134
if (auto *VD = dyn_cast<ValueDecl>(D)) {
131135
if (!isPublicOrUsableFromInline(VD)) {

lib/PrintAsObjC/PrintAsObjC.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,8 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
204204
}
205205

206206
bool shouldInclude(const ValueDecl *VD) {
207-
return isVisibleToObjC(VD, minRequiredAccess);
207+
return isVisibleToObjC(VD, minRequiredAccess) &&
208+
!VD->getAttrs().hasAttribute<ImplementationOnlyAttr>();
208209
}
209210

210211
private:

lib/Sema/TypeCheckAccess.cpp

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1654,6 +1654,9 @@ class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
16541654
explicit ExportabilityChecker(TypeChecker &TC) : TC(TC) {}
16551655

16561656
static bool shouldSkipChecking(const ValueDecl *VD) {
1657+
if (VD->getAttrs().hasAttribute<ImplementationOnlyAttr>())
1658+
return true;
1659+
16571660
// Accessors are handled as part of their Var or Subscript, and we don't
16581661
// want to redo extension signature checking for them.
16591662
if (isa<AccessorDecl>(VD))
@@ -1683,13 +1686,49 @@ class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
16831686
return true;
16841687
}
16851688

1689+
void checkOverride(const ValueDecl *VD) {
1690+
const ValueDecl *overridden = VD->getOverriddenDecl();
1691+
if (!overridden)
1692+
return;
1693+
1694+
auto *SF = VD->getDeclContext()->getParentSourceFile();
1695+
assert(SF && "checking a non-source declaration?");
1696+
1697+
ModuleDecl *M = overridden->getModuleContext();
1698+
if (SF->isImportedImplementationOnly(M)) {
1699+
TC.diagnose(VD, diag::implementation_only_override_import_without_attr,
1700+
overridden->getDescriptiveKind())
1701+
.fixItInsert(VD->getAttributeInsertionLoc(false),
1702+
"@_implementationOnly ");
1703+
TC.diagnose(overridden, diag::overridden_here);
1704+
return;
1705+
}
1706+
1707+
if (overridden->getAttrs().hasAttribute<ImplementationOnlyAttr>()) {
1708+
TC.diagnose(VD, diag::implementation_only_override_without_attr,
1709+
overridden->getDescriptiveKind())
1710+
.fixItInsert(VD->getAttributeInsertionLoc(false),
1711+
"@_implementationOnly ");
1712+
TC.diagnose(overridden, diag::overridden_here);
1713+
return;
1714+
}
1715+
1716+
// FIXME: Check storage decls where the setter is in a separate module from
1717+
// the getter, which is a thing Objective-C can do. The ClangImporter
1718+
// doesn't make this easy, though, because it just gives the setter the same
1719+
// DeclContext as the property or subscript, which means we've lost the
1720+
// information about whether its module was implementation-only imported.
1721+
}
1722+
16861723
void visit(Decl *D) {
16871724
if (D->isInvalid() || D->isImplicit())
16881725
return;
16891726

1690-
if (auto *VD = dyn_cast<ValueDecl>(D))
1727+
if (auto *VD = dyn_cast<ValueDecl>(D)) {
16911728
if (shouldSkipChecking(VD))
16921729
return;
1730+
checkOverride(VD);
1731+
}
16931732

16941733
DeclVisitor<ExportabilityChecker>::visit(D);
16951734
}
@@ -1731,13 +1770,16 @@ class ExportabilityChecker : public DeclVisitor<ExportabilityChecker> {
17311770
void checkNamedPattern(const NamedPattern *NP,
17321771
const llvm::DenseSet<const VarDecl *> &seenVars) {
17331772
const VarDecl *theVar = NP->getDecl();
1734-
// Only check individual variables if we didn't check an enclosing
1735-
// TypedPattern.
1736-
if (seenVars.count(theVar) || theVar->isInvalid())
1737-
return;
17381773
if (shouldSkipChecking(theVar))
17391774
return;
17401775

1776+
checkOverride(theVar);
1777+
1778+
// Only check the type of individual variables if we didn't check an
1779+
// enclosing TypedPattern.
1780+
if (seenVars.count(theVar) || theVar->isInvalid())
1781+
return;
1782+
17411783
checkType(theVar->getInterfaceType(), /*typeRepr*/nullptr, theVar,
17421784
getDiagnoseCallback(theVar), getDiagnoseCallback(theVar));
17431785
}

lib/Sema/TypeCheckAttr.cpp

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -776,7 +776,6 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
776776
IGNORED_ATTR(IBDesignable)
777777
IGNORED_ATTR(IBInspectable)
778778
IGNORED_ATTR(IBOutlet) // checked early.
779-
IGNORED_ATTR(ImplementationOnly)
780779
IGNORED_ATTR(ImplicitlyUnwrappedOptional)
781780
IGNORED_ATTR(Indirect)
782781
IGNORED_ATTR(Inline)
@@ -862,6 +861,8 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
862861
void visitCustomAttr(CustomAttr *attr);
863862
void visitPropertyWrapperAttr(PropertyWrapperAttr *attr);
864863
void visitFunctionBuilderAttr(FunctionBuilderAttr *attr);
864+
865+
void visitImplementationOnlyAttr(ImplementationOnlyAttr *attr);
865866
};
866867
} // end anonymous namespace
867868

@@ -2633,6 +2634,69 @@ void AttributeChecker::visitFunctionBuilderAttr(FunctionBuilderAttr *attr) {
26332634
// Any other validation?
26342635
}
26352636

2637+
void
2638+
AttributeChecker::visitImplementationOnlyAttr(ImplementationOnlyAttr *attr) {
2639+
if (isa<ImportDecl>(D)) {
2640+
// These are handled elsewhere.
2641+
return;
2642+
}
2643+
2644+
auto *VD = cast<ValueDecl>(D);
2645+
auto *overridden = VD->getOverriddenDecl();
2646+
if (!overridden) {
2647+
diagnoseAndRemoveAttr(attr, diag::implementation_only_decl_non_override);
2648+
return;
2649+
}
2650+
2651+
// Check if VD has the exact same type as what it overrides.
2652+
// Note: This is specifically not using `swift::getMemberTypeForComparison`
2653+
// because that erases more information than we want, like `throws`-ness.
2654+
auto baseInterfaceTy = overridden->getInterfaceType();
2655+
auto derivedInterfaceTy = VD->getInterfaceType();
2656+
2657+
auto selfInterfaceTy = VD->getDeclContext()->getDeclaredInterfaceType();
2658+
2659+
auto overrideInterfaceTy =
2660+
selfInterfaceTy->adjustSuperclassMemberDeclType(overridden, VD,
2661+
baseInterfaceTy);
2662+
2663+
if (isa<AbstractFunctionDecl>(VD)) {
2664+
// Drop the 'Self' parameter.
2665+
// FIXME: The real effect here, though, is dropping the generic signature.
2666+
// This should be okay because it should already be checked as part of
2667+
// making an override, but that isn't actually the case as of this writing,
2668+
// and it's kind of suspect anyway.
2669+
derivedInterfaceTy =
2670+
derivedInterfaceTy->castTo<AnyFunctionType>()->getResult();
2671+
overrideInterfaceTy =
2672+
overrideInterfaceTy->castTo<AnyFunctionType>()->getResult();
2673+
} else if (isa<SubscriptDecl>(VD)) {
2674+
// For subscripts, we don't have a 'Self' type, but turn it
2675+
// into a monomorphic function type.
2676+
// FIXME: does this actually make sense, though?
2677+
auto derivedInterfaceFuncTy = derivedInterfaceTy->castTo<AnyFunctionType>();
2678+
derivedInterfaceTy =
2679+
FunctionType::get(derivedInterfaceFuncTy->getParams(),
2680+
derivedInterfaceFuncTy->getResult());
2681+
auto overrideInterfaceFuncTy =
2682+
overrideInterfaceTy->castTo<AnyFunctionType>();
2683+
overrideInterfaceTy =
2684+
FunctionType::get(overrideInterfaceFuncTy->getParams(),
2685+
overrideInterfaceFuncTy->getResult());
2686+
}
2687+
2688+
if (!derivedInterfaceTy->isEqual(overrideInterfaceTy)) {
2689+
TC.diagnose(VD, diag::implementation_only_override_changed_type,
2690+
overrideInterfaceTy);
2691+
TC.diagnose(overridden, diag::overridden_here);
2692+
return;
2693+
}
2694+
2695+
// FIXME: When compiling without library evolution enabled, this should also
2696+
// check whether VD or any of its accessors need a new vtable entry, even if
2697+
// it won't necessarily be able to say why.
2698+
}
2699+
26362700
void TypeChecker::checkParameterAttributes(ParameterList *params) {
26372701
for (auto param: *params) {
26382702
checkDeclAttributes(param);

lib/Serialization/Deserialization.cpp

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1628,7 +1628,12 @@ ModuleFile::resolveCrossReference(ModuleID MID, uint32_t pathLen) {
16281628
return nullptr;
16291629
}
16301630
values.front() = storage->getAccessor(*actualKind);
1631-
assert(values.front() && "missing accessor");
1631+
if (!values.front()) {
1632+
return llvm::make_error<XRefError>("missing accessor",
1633+
pathTrace,
1634+
getXRefDeclNameForError());
1635+
1636+
}
16321637
}
16331638
break;
16341639
}
@@ -3021,10 +3026,27 @@ class swift::DeclDeserializer {
30213026
}
30223027
}
30233028

3024-
Expected<Decl *> overridden = MF.getDeclChecked(overriddenID);
3025-
if (!overridden) {
3026-
llvm::consumeError(overridden.takeError());
3027-
return llvm::make_error<OverrideError>(name, errorFlags);
3029+
Expected<Decl *> overriddenOrError = MF.getDeclChecked(overriddenID);
3030+
Decl *overridden;
3031+
if (overriddenOrError) {
3032+
overridden = overriddenOrError.get();
3033+
} else {
3034+
llvm::consumeError(overriddenOrError.takeError());
3035+
// There's one case where we know it's safe to ignore a missing override:
3036+
// if this declaration is '@objc' and 'dynamic'.
3037+
bool canIgnoreMissingOverriddenDecl = false;
3038+
if (isObjC && ctx.LangOpts.EnableDeserializationRecovery) {
3039+
canIgnoreMissingOverriddenDecl =
3040+
std::any_of(DeclAttributes::iterator(DAttrs),
3041+
DeclAttributes::iterator(nullptr),
3042+
[](const DeclAttribute *attr) -> bool {
3043+
return isa<DynamicAttr>(attr);
3044+
});
3045+
}
3046+
if (!canIgnoreMissingOverriddenDecl)
3047+
return llvm::make_error<OverrideError>(name, errorFlags);
3048+
3049+
overridden = nullptr;
30283050
}
30293051

30303052
for (TypeID dependencyID : dependencyIDs) {
@@ -3115,7 +3137,7 @@ class swift::DeclDeserializer {
31153137
if (auto bodyText = MF.maybeReadInlinableBodyText())
31163138
fn->setBodyStringRepresentation(*bodyText);
31173139

3118-
fn->setOverriddenDecl(cast_or_null<FuncDecl>(overridden.get()));
3140+
fn->setOverriddenDecl(cast_or_null<FuncDecl>(overridden));
31193141
if (fn->getOverriddenDecl())
31203142
AddAttribute(new (ctx) OverrideAttr(SourceLoc()));
31213143

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@import Foundation;
2+
3+
@interface NSObject (Secrets)
4+
- (void)secretMethod;
5+
@end

test/PrintAsObjC/Inputs/custom-modules/module.map

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,8 @@ module resilient_objc_class {
4747
header "resilient_objc_class.h"
4848
export *
4949
}
50+
51+
module MiserablePileOfSecrets {
52+
header "MiserablePileOfSecrets.h"
53+
export *
54+
}

test/PrintAsObjC/imports.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,16 @@
1717
// CHECK-NEXT: @import MostlyPrivate1;
1818
// CHECK-NEXT: @import MostlyPrivate1_Private;
1919
// CHECK-NEXT: @import MostlyPrivate2_Private;
20+
// CHECK-NEXT: @import ObjectiveC;
2021
// CHECK-NEXT: @import ctypes.bits;
2122

2223
// NEGATIVE-NOT: ctypes;
2324
// NEGATIVE-NOT: ImSub;
2425
// NEGATIVE-NOT: ImplicitSub;
2526
// NEGATIVE-NOT: MostlyPrivate2;
27+
// NEGATIVE-NOT: MiserablePileOfSecrets;
28+
29+
// NEGATIVE-NOT: secretMethod
2630

2731
import ctypes.bits
2832
import Foundation
@@ -40,6 +44,8 @@ import MostlyPrivate1_Private
4044
// Deliberately not importing MostlyPrivate2
4145
import MostlyPrivate2_Private
4246

47+
@_implementationOnly import MiserablePileOfSecrets
48+
4349
@objc class Test {
4450
@objc let word: DWORD = 0
4551
@objc let number: TimeInterval = 0.0
@@ -57,3 +63,7 @@ import MostlyPrivate2_Private
5763

5864
@objc let mp2priv: MP2PrivateType = 0
5965
}
66+
67+
@objc public class TestSubclass: NSObject {
68+
@_implementationOnly public override func secretMethod() {}
69+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
@interface Base
2+
@end
3+
4+
@interface Parent : Base
5+
- (nonnull instancetype)init __attribute__((objc_designated_initializer));
6+
7+
// The SECRET is on a non-secret property here because we need to enforce that
8+
// it's not printed.
9+
@property (readonly, strong, nullable) Parent *redefinedPropSECRET;
10+
@end
11+
12+
@interface GenericParent<T: Base *> : Base
13+
- (nonnull instancetype)init __attribute__((objc_designated_initializer));
14+
@end
15+
16+
@interface SubscriptParent : Base
17+
- (nullable Parent *)objectAtIndexedSubscript:(int)index;
18+
@end
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#import <FooKit.h>
2+
3+
@interface SECRETType : Base
4+
@end
5+
6+
@interface Parent ()
7+
- (nonnull instancetype)initWithSECRET:(int)secret __attribute__((objc_designated_initializer, swift_name("init(SECRET:)")));
8+
9+
- (void)methodSECRET;
10+
- (nullable SECRETType *)methodWithSECRETType;
11+
12+
@property (readonly, strong, nullable) Parent *roPropSECRET;
13+
@property (readwrite, strong, nullable) Parent *rwPropSECRET;
14+
15+
- (nullable Parent *)objectAtIndexedSubscript:(int)index;
16+
17+
@property (readwrite, strong, nullable) Parent *redefinedPropSECRET;
18+
@end
19+
20+
@protocol MandatorySecrets
21+
- (nonnull instancetype)initWithRequiredSECRET:(int)secret;
22+
@end
23+
24+
@interface Parent () <MandatorySecrets>
25+
- (nonnull instancetype)initWithRequiredSECRET:(int)secret __attribute__((objc_designated_initializer));
26+
@end
27+
28+
@interface GenericParent<T: Base *> ()
29+
@property (readonly, strong, nullable) T roPropSECRET;
30+
- (nullable Parent *)objectAtIndexedSubscript:(int)index;
31+
@end
32+
33+
@interface SubscriptParent ()
34+
- (void)setObject:(nullable Parent *)object atIndexedSubscript:(int)index;
35+
@end
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module FooKit {
2+
header "FooKit.h"
3+
export *
4+
}
5+
6+
module FooKit_SECRET {
7+
header "FooKit_SECRET.h"
8+
export *
9+
}

0 commit comments

Comments
 (0)