Skip to content

Commit 67b74a0

Browse files
committed
[Sema] RuntimeMetadata: Diagnose missing explicit reflection metadata attributes
Diagnose situations where a sub-class or a protocol do not have all of the reflection metadata attributes required by a superclass. Resolves: rdar://103990788
1 parent af639e5 commit 67b74a0

File tree

7 files changed

+161
-10
lines changed

7 files changed

+161
-10
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6949,6 +6949,17 @@ NOTE(missing_reflection_metadata_attribute_on_type,none,
69496949
"protocol %0 requires reflection metadata attribute @%1",
69506950
(DeclName, StringRef))
69516951

6952+
ERROR(missing_reflection_metadata_attribute_on_subclass,none,
6953+
"superclass %0 requires reflection metadata attribute @%1",
6954+
(DeclName, StringRef))
6955+
NOTE(add_missing_reflection_metadata_attr,none,
6956+
"add missing reflection metadata attribute @%0", (StringRef))
6957+
NOTE(opt_out_from_missing_reflection_metadata_attr,none,
6958+
"opt-out of reflection metadata attribute @%0 using"
6959+
" unavailable extension",
6960+
(StringRef))
6961+
6962+
69526963
#define UNDEFINE_DIAGNOSTIC_MACROS
69536964
#include "DefineDiagnosticMacros.h"
69546965

lib/AST/ASTPrinter.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2965,12 +2965,6 @@ static bool usesFeatureTypeWrappers(Decl *decl) {
29652965
}
29662966

29672967
static bool usesFeatureRuntimeDiscoverableAttrs(Decl *decl) {
2968-
if (decl->getAttrs().hasAttribute<RuntimeMetadataAttr>())
2969-
return true;
2970-
2971-
if (auto *VD = dyn_cast<ValueDecl>(decl))
2972-
return !VD->getRuntimeDiscoverableAttrs().empty();
2973-
29742968
return false;
29752969
}
29762970

lib/Sema/TypeCheckAttr.cpp

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7149,7 +7149,16 @@ void AttributeChecker::visitCompilerInitializedAttr(
71497149
}
71507150

71517151
void AttributeChecker::visitRuntimeMetadataAttr(RuntimeMetadataAttr *attr) {
7152-
if (!Ctx.LangOpts.hasFeature(Feature::RuntimeDiscoverableAttrs)) {
7152+
auto isEnabled = [&]() {
7153+
if (Ctx.LangOpts.hasFeature(Feature::RuntimeDiscoverableAttrs))
7154+
return true;
7155+
7156+
// Accept attributes that come from swiftinterface files.
7157+
auto *parentSF = D->getDeclContext()->getParentSourceFile();
7158+
return parentSF && parentSF->Kind == SourceFileKind::Interface;
7159+
};
7160+
7161+
if (!isEnabled()) {
71537162
diagnose(attr->getLocation(),
71547163
diag::runtime_discoverable_attrs_are_experimental);
71557164
attr->setInvalid();
@@ -7453,7 +7462,7 @@ ValueDecl *RenamedDeclRequest::evaluate(Evaluator &evaluator,
74537462

74547463
template <typename ATTR>
74557464
static void forEachCustomAttribute(
7456-
ValueDecl *decl,
7465+
Decl *decl,
74577466
llvm::function_ref<void(CustomAttr *attr, NominalTypeDecl *)> fn) {
74587467
auto &ctx = decl->getASTContext();
74597468

@@ -7513,6 +7522,57 @@ GetRuntimeDiscoverableAttributes::evaluate(Evaluator &evaluator,
75137522
return copy;
75147523
};
75157524

7525+
// Verify that a subclass or a protocol has all of the reflection
7526+
// metadata attributes expected by the superclass.
7527+
auto verifySuperclassAttrRequirements = [&](NominalTypeDecl *typeDecl,
7528+
ClassDecl *superclass) {
7529+
auto attrRequirements = superclass->getRuntimeDiscoverableAttrs();
7530+
if (attrRequirements.empty())
7531+
return;
7532+
7533+
// All of the reflection metadata attributes declared in
7534+
// unavailable extensions of this type in the same module.
7535+
llvm::SmallPtrSet<NominalTypeDecl *, 4> unavailableAttrs;
7536+
for (auto *extension : typeDecl->getExtensions()) {
7537+
if (extension->isConstrainedExtension() ||
7538+
extension->getParentModule() != typeDecl->getParentModule())
7539+
continue;
7540+
7541+
forEachCustomAttribute<RuntimeMetadataAttr>(
7542+
extension, [&](CustomAttr *attr, NominalTypeDecl *attrDecl) {
7543+
unavailableAttrs.insert(attrDecl);
7544+
});
7545+
}
7546+
7547+
for (auto *attr : attrRequirements) {
7548+
auto *attrDecl = superclass->getRuntimeDiscoverableAttrTypeDecl(attr);
7549+
if (attrs.count(attrDecl) || unavailableAttrs.count(attrDecl))
7550+
continue;
7551+
7552+
std::string attrName = attrDecl->getNameStr().str();
7553+
std::string subclassName = typeDecl->getNameStr().str();
7554+
7555+
ctx.Diags.diagnose(
7556+
typeDecl, diag::missing_reflection_metadata_attribute_on_subclass,
7557+
superclass->getName(), attrName);
7558+
7559+
ctx.Diags
7560+
.diagnose(typeDecl, diag::add_missing_reflection_metadata_attr,
7561+
attrName)
7562+
.fixItInsert(
7563+
typeDecl->getAttributeInsertionLoc(/*forModifier=*/false),
7564+
"@" + attrName + " ");
7565+
7566+
ctx.Diags
7567+
.diagnose(typeDecl,
7568+
diag::opt_out_from_missing_reflection_metadata_attr,
7569+
attrName)
7570+
.fixItInsertAfter(typeDecl->getEndLoc(),
7571+
"\n\n@available(*, unavailable)\n@" + attrName +
7572+
" extension " + subclassName + " {}\n");
7573+
}
7574+
};
7575+
75167576
// Gather reflection metadata attributes only if this extension is:
75177577
// - unavailable;
75187578
// - unconstrained;
@@ -7530,10 +7590,18 @@ GetRuntimeDiscoverableAttributes::evaluate(Evaluator &evaluator,
75307590
gatherRuntimeAttrsOnDecl(decl, attrs, GatheringMode::Direct);
75317591

75327592
auto *NTD = dyn_cast<NominalTypeDecl>(decl);
7533-
// Attribute inference is only possible from protocol conformances.
7534-
if (!NTD || isa<ProtocolDecl>(NTD) || NTD->getDeclContext()->isLocalContext())
7593+
if (!NTD || NTD->getDeclContext()->isLocalContext())
75357594
return copyAttrs(attrs);
75367595

7596+
// If this is a protocol, let's check whether superclass
7597+
// has any reflection metadata attribute requirements.
7598+
if (auto *protocol = dyn_cast<ProtocolDecl>(NTD)) {
7599+
if (auto *superclass = protocol->getSuperclassDecl())
7600+
verifySuperclassAttrRequirements(NTD, superclass);
7601+
7602+
return copyAttrs(attrs);
7603+
}
7604+
75377605
// Gather any attributes inferred from (explicit) protocol conformances
75387606
// associated with the declaration of the type.
75397607
for (unsigned i : indices(NTD->getInherited())) {
@@ -7551,6 +7619,12 @@ GetRuntimeDiscoverableAttributes::evaluate(Evaluator &evaluator,
75517619
gatherRuntimeAttrsOnDecl(protocol, attrs, GatheringMode::Inference);
75527620
}
75537621

7622+
// Check superclass attribute requirements after inference.
7623+
if (auto *classDecl = dyn_cast<ClassDecl>(NTD)) {
7624+
if (auto *superclass = classDecl->getSuperclassDecl())
7625+
verifySuperclassAttrRequirements(NTD, superclass);
7626+
}
7627+
75547628
return copyAttrs(attrs);
75557629
}
75567630

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-emit-module-interface(%t/ReflectionMetadata.swiftinterface) %s -module-name CRM -enable-experimental-feature RuntimeDiscoverableAttrs
3+
// RUN: %target-swift-typecheck-module-from-interface(%t/ReflectionMetadata.swiftinterface) -module-name CRM
4+
// RUN: %FileCheck %s < %t/ReflectionMetadata.swiftinterface
5+
6+
// REQUIRES: asserts
7+
8+
@runtimeMetadata
9+
public struct Flag {
10+
public init<T>(attachedTo: T) {}
11+
}
12+
13+
@Flag
14+
public class BaseClass {}
15+
16+
// CHECK: public class TestClass : CRM.BaseClass
17+
18+
public class TestClass : BaseClass {}
19+
20+
// CHECK: @available(*, unavailable)
21+
// CHECK-NEXT: @Flag extension CRM.TestClass {
22+
// CHECK-NEXT: }
23+
24+
@available(*, unavailable)
25+
@Flag
26+
public extension TestClass {
27+
}

test/SILGen/Inputs/runtime_metadata_defs.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,14 @@ public struct Ignore {
88

99
@Ignore
1010
public protocol Ignorable {}
11+
12+
public class Base : Ignorable {
13+
public init() {}
14+
}
15+
16+
public class Child : Base {
17+
}
18+
19+
@available(*, unavailable)
20+
@Ignore
21+
extension Child {}

test/SILGen/runtime_attributes.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,6 @@ struct TestSelfUse {
105105
// CHECK-NEXT: {{.*}} = apply [[FLAG_INIT_REF]]<String, (TestSelfUse) -> ()>({{.*}}, [[PROP_VAL_COPY]], [[FUNC_NAME_STR]], {{.*}})
106106
@Flag(value: Self.question) func test() {}
107107
}
108+
109+
// This make sure that Child is valid even though it opted-out of attribute.
110+
_ = Child()

test/type/runtime_discoverable_attrs.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,3 +318,34 @@ class MultiAttrTest {
318318
}
319319

320320
extension MultiAttrTest : Flagged & EnumFlagged {} // Ok
321+
322+
@Flag
323+
class BaseClass {}
324+
325+
@Flag
326+
class ValidChild : BaseClass {} // Ok
327+
328+
class InvalidChild : BaseClass {}
329+
// expected-error@-1 {{superclass 'BaseClass' requires reflection metadata attribute @Flag}}
330+
// expected-note@-2 {{add missing reflection metadata attribute @Flag}} {{1-1=@Flag }}
331+
// expected-note@-3 {{opt-out of reflection metadata attribute @Flag using unavailable extension}} {{34-34=\n\n@available(*, unavailable)\n@Flag extension InvalidChild {\}\n}}
332+
333+
class OptedOutChild : BaseClass {} // Ok (used unavailable extension to opt-out of the attribute)
334+
335+
@available(*, unavailable)
336+
@Flag
337+
extension OptedOutChild {}
338+
339+
protocol InvalidProtoWithSuperclass : BaseClass {}
340+
// expected-error@-1 {{superclass 'BaseClass' requires reflection metadata attribute @Flag}}
341+
// expected-note@-2 {{add missing reflection metadata attribute @Flag}} {{1-1=@Flag }}
342+
// expected-note@-3 {{opt-out of reflection metadata attribute @Flag using unavailable extension}} {{51-51=\n\n@available(*, unavailable)\n@Flag extension InvalidProtoWithSuperclass {\}\n}}
343+
344+
@Flag
345+
protocol ValidProtoWithSuperclass : BaseClass {} // Ok
346+
347+
protocol OptedOutProtoWithSuperclass : BaseClass {} // Ok
348+
349+
@available(*, unavailable)
350+
@Flag
351+
extension OptedOutProtoWithSuperclass {}

0 commit comments

Comments
 (0)