Skip to content

Commit 41fe367

Browse files
committed
[CodeCompletion] Don't skip derived conformance members on override completion lookup
1 parent f724d1f commit 41fe367

11 files changed

+254
-84
lines changed

include/swift/AST/Decl.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4197,6 +4197,22 @@ struct SelfReferenceKind {
41974197
other(other) { }
41984198
};
41994199

4200+
/// The set of known protocols for which derived conformances are supported.
4201+
enum class KnownDerivableProtocolKind : uint8_t {
4202+
RawRepresentable,
4203+
OptionSet,
4204+
CaseIterable,
4205+
Comparable,
4206+
Equatable,
4207+
Hashable,
4208+
BridgedNSError,
4209+
CodingKey,
4210+
Encodable,
4211+
Decodable,
4212+
AdditiveArithmetic,
4213+
Differentiable,
4214+
};
4215+
42004216
/// ProtocolDecl - A declaration of a protocol, for example:
42014217
///
42024218
/// protocol Drawable {
@@ -4409,6 +4425,8 @@ class ProtocolDecl final : public NominalTypeDecl {
44094425
return static_cast<KnownProtocolKind>(Bits.ProtocolDecl.KnownProtocol - 2);
44104426
}
44114427

4428+
Optional<KnownDerivableProtocolKind> getKnownDerivableProtocolKind() const;
4429+
44124430
/// Check whether this protocol is of a specific, known protocol kind.
44134431
bool isSpecificProtocol(KnownProtocolKind kind) const {
44144432
if (auto knownKind = getKnownProtocolKind())

include/swift/AST/NameLookup.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -263,9 +263,9 @@ enum class DeclVisibilityKind {
263263
/// \endcode
264264
MemberOfCurrentNominal,
265265

266-
/// Declaration that is a requirement of a protocol implemented by the
267-
/// immediately enclosing nominal decl, in case the nominal decl does not
268-
/// supply a witness for this requirement.
266+
/// Declaration is a requirement – in case the nominal decl does not supply
267+
/// a corresponding witness – or an extension member of a protocol
268+
/// conformed to by the immediately enclosing nominal decl.
269269
///
270270
/// For example, 'foo' is visible at (1) because of this.
271271
/// \code
@@ -278,7 +278,12 @@ enum class DeclVisibilityKind {
278278
/// }
279279
/// }
280280
/// \endcode
281-
MemberOfProtocolImplementedByCurrentNominal,
281+
MemberOfProtocolConformedToByCurrentNominal,
282+
283+
/// Declaration is a derived requirement of a protocol conformed to by the
284+
/// immediately enclosing nominal decl (a witness for a synthesized
285+
/// conformance).
286+
MemberOfProtocolDerivedByCurrentNominal,
282287

283288
/// Declaration is a member of the superclass of the immediately enclosing
284289
/// nominal decl.
@@ -467,6 +472,7 @@ void lookupVisibleMemberDecls(VisibleDeclConsumer &Consumer,
467472
Type BaseTy,
468473
const DeclContext *CurrDC,
469474
bool includeInstanceMembers,
475+
bool includeDerivedRequirements,
470476
GenericSignatureBuilder *GSB = nullptr);
471477

472478
namespace namelookup {

lib/AST/Decl.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5049,6 +5049,41 @@ void ProtocolDecl::computeKnownProtocolKind() const {
50495049
const_cast<ProtocolDecl *>(this)->Bits.ProtocolDecl.KnownProtocol = value;
50505050
}
50515051

5052+
Optional<KnownDerivableProtocolKind>
5053+
ProtocolDecl::getKnownDerivableProtocolKind() const {
5054+
const auto knownKind = getKnownProtocolKind();
5055+
if (!knownKind)
5056+
return None;
5057+
5058+
switch (*knownKind) {
5059+
case KnownProtocolKind::RawRepresentable:
5060+
return KnownDerivableProtocolKind::RawRepresentable;
5061+
case KnownProtocolKind::OptionSet:
5062+
return KnownDerivableProtocolKind::OptionSet;
5063+
case KnownProtocolKind::CaseIterable:
5064+
return KnownDerivableProtocolKind::CaseIterable;
5065+
case KnownProtocolKind::Comparable:
5066+
return KnownDerivableProtocolKind::Comparable;
5067+
case KnownProtocolKind::Equatable:
5068+
return KnownDerivableProtocolKind::Equatable;
5069+
case KnownProtocolKind::Hashable:
5070+
return KnownDerivableProtocolKind::Hashable;
5071+
case KnownProtocolKind::BridgedNSError:
5072+
return KnownDerivableProtocolKind::BridgedNSError;
5073+
case KnownProtocolKind::CodingKey:
5074+
return KnownDerivableProtocolKind::CodingKey;
5075+
case KnownProtocolKind::Encodable:
5076+
return KnownDerivableProtocolKind::Encodable;
5077+
case KnownProtocolKind::Decodable:
5078+
return KnownDerivableProtocolKind::Decodable;
5079+
case KnownProtocolKind::AdditiveArithmetic:
5080+
return KnownDerivableProtocolKind::AdditiveArithmetic;
5081+
case KnownProtocolKind::Differentiable:
5082+
return KnownDerivableProtocolKind::Differentiable;
5083+
default: return None;
5084+
}
5085+
}
5086+
50525087
bool ProtocolDecl::hasCircularInheritedProtocols() const {
50535088
auto &ctx = getASTContext();
50545089
auto *mutableThis = const_cast<ProtocolDecl *>(this);

lib/IDE/CodeCompletion.cpp

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1847,7 +1847,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
18471847
return SemanticContextKind::ExpressionSpecific;
18481848
return SemanticContextKind::CurrentNominal;
18491849

1850-
case DeclVisibilityKind::MemberOfProtocolImplementedByCurrentNominal:
1850+
case DeclVisibilityKind::MemberOfProtocolConformedToByCurrentNominal:
18511851
case DeclVisibilityKind::MemberOfSuper:
18521852
return SemanticContextKind::Super;
18531853

@@ -1891,6 +1891,9 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
18911891
D, dynamicLookupInfo.getKeyPathDynamicMember().originalVisibility,
18921892
{});
18931893
}
1894+
1895+
case DeclVisibilityKind::MemberOfProtocolDerivedByCurrentNominal:
1896+
llvm_unreachable("should not see this kind");
18941897
}
18951898
llvm_unreachable("unhandled kind");
18961899
}
@@ -3052,6 +3055,10 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
30523055
// Implement swift::VisibleDeclConsumer.
30533056
void foundDecl(ValueDecl *D, DeclVisibilityKind Reason,
30543057
DynamicLookupInfo dynamicLookupInfo) override {
3058+
assert(Reason !=
3059+
DeclVisibilityKind::MemberOfProtocolDerivedByCurrentNominal &&
3060+
"Including derived requirement in non-override lookup");
3061+
30553062
if (D->shouldHideFromEditor())
30563063
return;
30573064

@@ -3328,7 +3335,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
33283335
if (isIUO) {
33293336
if (Type Unwrapped = ExprType->getOptionalObjectType()) {
33303337
lookupVisibleMemberDecls(*this, Unwrapped, CurrDeclContext,
3331-
IncludeInstanceMembers);
3338+
IncludeInstanceMembers,
3339+
/*includeDerivedRequirements*/false);
33323340
return true;
33333341
}
33343342
assert(IsUnwrappedOptional && "IUOs should be optional if not bound/forced");
@@ -3348,7 +3356,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
33483356
CodeCompletionResult::MaxNumBytesToErase) {
33493357
if (!tryTupleExprCompletions(Unwrapped)) {
33503358
lookupVisibleMemberDecls(*this, Unwrapped, CurrDeclContext,
3351-
IncludeInstanceMembers);
3359+
IncludeInstanceMembers,
3360+
/*includeDerivedRequirements*/false);
33523361
}
33533362
}
33543363
return true;
@@ -3418,7 +3427,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
34183427
tryUnwrappedCompletions(ExprType, isIUO);
34193428

34203429
lookupVisibleMemberDecls(*this, ExprType, CurrDeclContext,
3421-
IncludeInstanceMembers);
3430+
IncludeInstanceMembers,
3431+
/*includeDerivedRequirements*/false);
34223432
}
34233433

34243434
void collectOperators(SmallVectorImpl<OperatorDecl *> &results) {
@@ -3893,7 +3903,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
38933903
llvm::SaveAndRestore<Type> SaveType(ExprType, baseType);
38943904
llvm::SaveAndRestore<bool> SaveUnresolved(IsUnresolvedMember, true);
38953905
lookupVisibleMemberDecls(consumer, baseType, CurrDeclContext,
3896-
/*includeInstanceMembers=*/false);
3906+
/*includeInstanceMembers=*/false,
3907+
/*includeDerivedRequirements*/false);
38973908
}
38983909

38993910
void getUnresolvedMemberCompletions(ArrayRef<Type> Types) {
@@ -3954,7 +3965,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
39543965
NeedLeadingDot = !HaveDot;
39553966
lookupVisibleMemberDecls(*this, MetatypeType::get(BaseType),
39563967
CurrDeclContext,
3957-
IncludeInstanceMembers);
3968+
IncludeInstanceMembers,
3969+
/*includeDerivedRequirements*/false);
39583970
if (BaseType->isAnyExistentialType()) {
39593971
addKeyword("Protocol", MetatypeType::get(BaseType));
39603972
addKeyword("Type", ExistentialMetatypeType::get(BaseType));
@@ -3987,7 +3999,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
39873999
this->BaseType = selfTy;
39884000
NeedLeadingDot = false;
39894001
lookupVisibleMemberDecls(*this, MetatypeType::get(selfTy),
3990-
CurrDeclContext, IncludeInstanceMembers);
4002+
CurrDeclContext, IncludeInstanceMembers,
4003+
/*includeDerivedRequirements*/false);
39914004
}
39924005

39934006
static bool canUseAttributeOnDecl(DeclAttrKind DAK, bool IsInSil,
@@ -4253,7 +4266,7 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer {
42534266
Type getOpaqueResultType(const ValueDecl *VD, DeclVisibilityKind Reason,
42544267
DynamicLookupInfo dynamicLookupInfo) {
42554268
if (Reason !=
4256-
DeclVisibilityKind::MemberOfProtocolImplementedByCurrentNominal)
4269+
DeclVisibilityKind::MemberOfProtocolConformedToByCurrentNominal)
42574270
return nullptr;
42584271

42594272
auto currTy = CurrDeclContext->getDeclaredTypeInContext();
@@ -4469,12 +4482,18 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer {
44694482
bool needRequired = false;
44704483
auto C = CurrDeclContext->getSelfClassDecl();
44714484
if (C && !isKeywordSpecified("required")) {
4472-
if (Reason ==
4473-
DeclVisibilityKind::MemberOfProtocolImplementedByCurrentNominal &&
4474-
!C->isFinal())
4475-
needRequired = true;
4476-
else if (Reason == DeclVisibilityKind::MemberOfSuper && CD->isRequired())
4477-
needRequired = true;
4485+
switch (Reason) {
4486+
case DeclVisibilityKind::MemberOfProtocolConformedToByCurrentNominal:
4487+
case DeclVisibilityKind::MemberOfProtocolDerivedByCurrentNominal:
4488+
if (!C->isFinal())
4489+
needRequired = true;
4490+
break;
4491+
case DeclVisibilityKind::MemberOfSuper:
4492+
if (CD->isRequired())
4493+
needRequired = true;
4494+
break;
4495+
default: break;
4496+
}
44784497
}
44794498

44804499
llvm::SmallString<256> DeclStr;
@@ -4594,7 +4613,7 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer {
45944613
continue;
45954614
addTypeAlias(
45964615
ATD,
4597-
DeclVisibilityKind::MemberOfProtocolImplementedByCurrentNominal,
4616+
DeclVisibilityKind::MemberOfProtocolConformedToByCurrentNominal,
45984617
{});
45994618
}
46004619
}
@@ -4612,7 +4631,8 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer {
46124631
// Look for overridable static members too.
46134632
Type Meta = MetatypeType::get(CurrTy);
46144633
lookupVisibleMemberDecls(*this, Meta, CurrDeclContext,
4615-
/*includeInstanceMembers=*/true);
4634+
/*includeInstanceMembers=*/true,
4635+
/*includeDerivedRequirements*/true);
46164636
addDesignatedInitializers(NTD);
46174637
addAssociatedTypes(NTD);
46184638
}

lib/IDE/ConformingMethodList.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,8 @@ void ConformingMethodListCallbacks::getMatchingMethods(
164164
} LocalConsumer(CurDeclContext, T, expectedTypes, result);
165165

166166
lookupVisibleMemberDecls(LocalConsumer, MetatypeType::get(T), CurDeclContext,
167-
/*includeInstanceMembers=*/false);
167+
/*includeInstanceMembers=*/false,
168+
/*includeDerivedRequirements*/false);
168169
}
169170

170171
} // anonymous namespace.

lib/IDE/TypeContextInfo.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,8 @@ void ContextInfoCallbacks::getImplicitMembers(
167167
} LocalConsumer(CurDeclContext, T, Result);
168168

169169
lookupVisibleMemberDecls(LocalConsumer, MetatypeType::get(T), CurDeclContext,
170-
/*includeInstanceMembers=*/false);
170+
/*includeInstanceMembers=*/false,
171+
/*includeDerivedRequirements*/false);
171172
}
172173

173174
void PrintingTypeContextInfoConsumer::handleResults(

lib/Sema/DerivedConformances.cpp

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -54,56 +54,61 @@ Type DerivedConformance::getProtocolType() const {
5454
bool DerivedConformance::derivesProtocolConformance(DeclContext *DC,
5555
NominalTypeDecl *Nominal,
5656
ProtocolDecl *Protocol) {
57-
// Only known protocols can be derived.
58-
auto knownProtocol = Protocol->getKnownProtocolKind();
59-
if (!knownProtocol)
57+
const auto derivableKind = Protocol->getKnownDerivableProtocolKind();
58+
if (!derivableKind)
6059
return false;
6160

62-
if (*knownProtocol == KnownProtocolKind::Hashable) {
61+
// When the necessary requirements are met, the conformance to OptionSet
62+
// is serendipitously derived via memberwise initializer synthesis.
63+
if (*derivableKind == KnownDerivableProtocolKind::OptionSet) {
64+
return false;
65+
}
66+
67+
if (*derivableKind == KnownDerivableProtocolKind::Hashable) {
6368
// We can always complete a partial Hashable implementation, and we can
6469
// synthesize a full Hashable implementation for structs and enums with
6570
// Hashable components.
6671
return canDeriveHashable(Nominal);
6772
}
6873

69-
if (*knownProtocol == KnownProtocolKind::AdditiveArithmetic)
74+
if (*derivableKind == KnownDerivableProtocolKind::AdditiveArithmetic)
7075
return canDeriveAdditiveArithmetic(Nominal, DC);
7176

72-
if (*knownProtocol == KnownProtocolKind::Differentiable)
77+
if (*derivableKind == KnownDerivableProtocolKind::Differentiable)
7378
return canDeriveDifferentiable(Nominal, DC);
7479

7580
if (auto *enumDecl = dyn_cast<EnumDecl>(Nominal)) {
76-
switch (*knownProtocol) {
81+
switch (*derivableKind) {
7782
// The presence of a raw type is an explicit declaration that
7883
// the compiler should derive a RawRepresentable conformance.
79-
case KnownProtocolKind::RawRepresentable:
84+
case KnownDerivableProtocolKind::RawRepresentable:
8085
return canDeriveRawRepresentable(DC, Nominal);
8186

8287
// Enums without associated values can implicitly derive Equatable
8388
// conformance.
84-
case KnownProtocolKind::Equatable:
89+
case KnownDerivableProtocolKind::Equatable:
8590
return canDeriveEquatable(DC, Nominal);
8691

87-
case KnownProtocolKind::Comparable:
92+
case KnownDerivableProtocolKind::Comparable:
8893
return !enumDecl->hasPotentiallyUnavailableCaseValue()
8994
&& canDeriveComparable(DC, enumDecl);
9095

9196
// "Simple" enums without availability attributes can explicitly derive
9297
// a CaseIterable conformance.
9398
//
9499
// FIXME: Lift the availability restriction.
95-
case KnownProtocolKind::CaseIterable:
100+
case KnownDerivableProtocolKind::CaseIterable:
96101
return !enumDecl->hasPotentiallyUnavailableCaseValue()
97102
&& enumDecl->hasOnlyCasesWithoutAssociatedValues();
98103

99104
// @objc enums can explicitly derive their _BridgedNSError conformance.
100-
case KnownProtocolKind::BridgedNSError:
105+
case KnownDerivableProtocolKind::BridgedNSError:
101106
return enumDecl->isObjC() && enumDecl->hasCases()
102107
&& enumDecl->hasOnlyCasesWithoutAssociatedValues();
103108

104109
// Enums without associated values and enums with a raw type of String
105110
// or Int can explicitly derive CodingKey conformance.
106-
case KnownProtocolKind::CodingKey: {
111+
case KnownDerivableProtocolKind::CodingKey: {
107112
Type rawType = enumDecl->getRawType();
108113
if (rawType) {
109114
auto parentDC = enumDecl->getDeclContext();
@@ -125,8 +130,8 @@ bool DerivedConformance::derivesProtocolConformance(DeclContext *DC,
125130
// Structs and classes can explicitly derive Encodable and Decodable
126131
// conformance (explicitly meaning we can synthesize an implementation if
127132
// a type conforms manually).
128-
if (*knownProtocol == KnownProtocolKind::Encodable ||
129-
*knownProtocol == KnownProtocolKind::Decodable) {
133+
if (*derivableKind == KnownDerivableProtocolKind::Encodable ||
134+
*derivableKind == KnownDerivableProtocolKind::Decodable) {
130135
// FIXME: This is not actually correct. We cannot promise to always
131136
// provide a witness here for all structs and classes. Unfortunately,
132137
// figuring out whether this is actually possible requires much more
@@ -140,8 +145,8 @@ bool DerivedConformance::derivesProtocolConformance(DeclContext *DC,
140145

141146
// Structs can explicitly derive Equatable conformance.
142147
if (isa<StructDecl>(Nominal)) {
143-
switch (*knownProtocol) {
144-
case KnownProtocolKind::Equatable:
148+
switch (*derivableKind) {
149+
case KnownDerivableProtocolKind::Equatable:
145150
return canDeriveEquatable(DC, Nominal);
146151
default:
147152
return false;

0 commit comments

Comments
 (0)