Skip to content

Commit 5948ba1

Browse files
committed
Add @_requiresConcreteImplementation attribute
1 parent 2dd7470 commit 5948ba1

File tree

6 files changed

+74
-4
lines changed

6 files changed

+74
-4
lines changed

include/swift/AST/Attr.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,12 @@ DECL_ATTR(transpose, Transpose,
552552
ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIBreakingToRemove,
553553
99)
554554

555+
SIMPLE_DECL_ATTR(_requiresConcreteImplementation, RequiresConcreteImplementation,
556+
OnFunc | OnAccessor | OnVar | OnSubscript | OnConstructor |
557+
LongAttribute | UserInaccessible | NotSerialized |
558+
ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
559+
100)
560+
555561
#undef TYPE_ATTR
556562
#undef DECL_ATTR_ALIAS
557563
#undef CONTEXTUAL_DECL_ATTR_ALIAS

include/swift/AST/DiagnosticsSema.def

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2116,8 +2116,8 @@ NOTE(declared_protocol_conformance_here,none,
21162116
(Type, unsigned, DeclName, DeclName))
21172117

21182118
ERROR(witness_unavailable,none,
2119-
"unavailable %0 %1 was used to satisfy a requirement of protocol %2",
2120-
(DescriptiveDeclKind, DeclName, DeclName))
2119+
"unavailable %0 %1 was used to satisfy a requirement of protocol %2",
2120+
(DescriptiveDeclKind, DeclName, DeclName))
21212121

21222122
ERROR(redundant_conformance,none,
21232123
"redundant conformance of %0 to protocol %1", (Type, DeclName))
@@ -2900,6 +2900,19 @@ ERROR(implements_attr_non_protocol_type,none,
29002900
ERROR(implements_attr_protocol_not_conformed_to,none,
29012901
"containing type %0 does not conform to protocol %1",
29022902
(DeclName, DeclName))
2903+
2904+
// @_requiresConcreteImplementation
2905+
ERROR(requires_concrete_implementation_attr_wrong_decl_context,none,
2906+
"'@_requiresConcreteImplementation' can only be specified on protocol "
2907+
"requirements", ())
2908+
ERROR(witness_must_be_concrete_implementation,none,
2909+
"protocol requirement marked '@_requiresConcreteImplementation' cannot "
2910+
"be satisfied by %0 %1 declared in protocol extension",
2911+
(DescriptiveDeclKind, DeclName))
2912+
ERROR(override_not_requiring_concrete_implementation,none,
2913+
"override of protocol requirement marked "
2914+
"'@_requiresConcreteImplementation' should also be marked "
2915+
"'@_requiresConcreteImplementation'", ())
29032916

29042917
// @differentiable
29052918
ERROR(differentiable_attr_no_vjp_or_jvp_when_linear,none,

lib/Sema/TypeCheckAttr.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,9 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
254254

255255
void visitDifferentiableAttr(DifferentiableAttr *attr);
256256
void visitDerivativeAttr(DerivativeAttr *attr);
257+
258+
void visitRequiresConcreteImplementationAttr(
259+
RequiresConcreteImplementationAttr *attr);
257260
};
258261
} // end anonymous namespace
259262

@@ -4551,3 +4554,10 @@ void AttributeChecker::visitDerivativeAttr(DerivativeAttr *attr) {
45514554
if (typeCheckDerivativeAttr(Ctx, D, attr))
45524555
attr->setInvalid();
45534556
}
4557+
4558+
void AttributeChecker::visitRequiresConcreteImplementationAttr(
4559+
RequiresConcreteImplementationAttr *attr) {
4560+
if (!isa<ProtocolDecl>(D->getDeclContext()))
4561+
diagnoseAndRemoveAttr(attr,
4562+
diag::requires_concrete_implementation_attr_wrong_decl_context);
4563+
}

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1539,6 +1539,21 @@ namespace {
15391539
.fixItInsert(Base->getAttributeInsertionLoc(false),
15401540
"@objc ");
15411541
}
1542+
1543+
void visitRequiresConcreteImplementationAttr(
1544+
RequiresConcreteImplementationAttr *attr) {
1545+
// Require '@_requiresConcreteImplementation' on the override if it was
1546+
// there on the base.
1547+
if (!Base->getAttrs().hasAttribute<RequiresConcreteImplementationAttr>())
1548+
return;
1549+
1550+
if (Override->getAttrs().hasAttribute<RequiresConcreteImplementationAttr>())
1551+
return;
1552+
1553+
Diags.diagnose(Override,
1554+
diag::override_not_requiring_concrete_implementation);
1555+
Diags.diagnose(Base, diag::overridden_here);
1556+
}
15421557
};
15431558
} // end anonymous namespace
15441559

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1377,6 +1377,13 @@ RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement,
13771377
return CheckKind::WitnessUnavailable;
13781378
}
13791379

1380+
// If a requirement has the @_requiresConcreteImplementation attribute, it
1381+
// cannot be satisfied by a default implementation in a protocol extension.
1382+
if (requirement->getAttrs().hasAttribute<RequiresConcreteImplementationAttr>()
1383+
&& match.Witness->getDeclContext()->getExtendedProtocolDecl()) {
1384+
return CheckKind::RequiresConcreteImplementation;
1385+
}
1386+
13801387
return CheckKind::Success;
13811388
}
13821389

@@ -3433,8 +3440,7 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
34333440

34343441
case CheckKind::WitnessUnavailable:
34353442
diagnoseOrDefer(requirement, /*isError=*/true,
3436-
[witness, requirement](
3437-
NormalProtocolConformance *conformance) {
3443+
[witness, requirement](NormalProtocolConformance *conformance) {
34383444
auto &diags = witness->getASTContext().Diags;
34393445
SourceLoc diagLoc = getLocForDiagnosingWitness(conformance, witness);
34403446
diags.diagnose(diagLoc,
@@ -3448,6 +3454,22 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
34483454
requirement->getFullName());
34493455
});
34503456
break;
3457+
3458+
case CheckKind::RequiresConcreteImplementation:
3459+
diagnoseOrDefer(requirement, false,
3460+
[witness, requirement](NormalProtocolConformance *conformance) {
3461+
auto &diags = witness->getASTContext().Diags;
3462+
SourceLoc diagLoc = getLocForDiagnosingWitness(conformance, witness);
3463+
diags.diagnose(diagLoc,
3464+
diag::witness_must_be_concrete_implementation,
3465+
witness->getDescriptiveKind(),
3466+
witness->getFullName());
3467+
emitDeclaredHereIfNeeded(diags, diagLoc, witness);
3468+
diags.diagnose(requirement, diag::kind_declname_declared_here,
3469+
DescriptiveDeclKind::Requirement,
3470+
requirement->getFullName());
3471+
});
3472+
break;
34513473
}
34523474

34533475
if (auto *classDecl = Adoptee->getClassOrBoundGenericClass()) {

lib/Sema/TypeCheckProtocol.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,10 @@ enum class CheckKind : unsigned {
278278

279279
/// The witness itself is inaccessible.
280280
WitnessUnavailable,
281+
282+
/// The requirement was marked '@_requiresConcreteImplementation', but the
283+
/// witness is a default implementation in a protocol extension.
284+
RequiresConcreteImplementation,
281285
};
282286

283287
/// Describes an optional adjustment made to a witness.

0 commit comments

Comments
 (0)