Skip to content

Commit 7c3f50d

Browse files
committed
[Conformance checking] Minor refactor for handling unsatisfied requirement diagnostics. NFC
1 parent 42357b6 commit 7c3f50d

File tree

1 file changed

+63
-47
lines changed

1 file changed

+63
-47
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 63 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4337,6 +4337,20 @@ static void diagnosePotentialWitness(TypeChecker &tc, ValueDecl *req,
43374337
tc.diagnose(req, diag::protocol_requirement_here, req->getFullName());
43384338
}
43394339

4340+
/// Determine whether the given requirement was left unsatisfied.
4341+
static bool isUnsatisfiedReq(NormalProtocolConformance *conformance,
4342+
ValueDecl *req) {
4343+
if (conformance->isInvalid()) return false;
4344+
if (isa<TypeDecl>(req)) return false;
4345+
4346+
// An optional requirement might not have a witness...
4347+
if (!conformance->hasWitness(req) ||
4348+
!conformance->getWitness(req, nullptr).getDecl())
4349+
return req->getAttrs().hasAttribute<OptionalAttr>();
4350+
4351+
return false;
4352+
}
4353+
43404354
void TypeChecker::checkConformancesInContext(DeclContext *dc,
43414355
IterableDeclContext *idc) {
43424356
// Determine the accessibility of this conformance.
@@ -4364,27 +4378,35 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc,
43644378
auto conformances = dc->getLocalConformances(ConformanceLookupKind::All,
43654379
&diagnostics,
43664380
/*sorted=*/true);
4367-
bool hasAnyUnsatisfiedOptionalReqs = false;
4381+
// Catalog all of members of this declaration context that satisfy
4382+
// requirements of conformances in this context.
4383+
llvm::MapVector<DeclName, llvm::TinyPtrVector<ValueDecl *>>
4384+
unsatisfiedReqs;
4385+
4386+
bool anyInvalid = false;
43684387
for (auto conformance : conformances) {
43694388
// Check and record normal conformances.
43704389
if (auto normal = dyn_cast<NormalProtocolConformance>(conformance)) {
43714390
checkConformance(normal);
43724391

4373-
// Check whether there are any unsatisfied optional requirements.
4374-
if (!normal->isInvalid() && !hasAnyUnsatisfiedOptionalReqs) {
4375-
auto proto = conformance->getProtocol();
4376-
for (auto member : proto->getMembers()) {
4377-
if (isa<TypeDecl>(member)) continue;
4392+
if (anyInvalid) continue;
43784393

4379-
auto value = dyn_cast<ValueDecl>(member);
4380-
if (!value) continue;
4394+
if (normal->isInvalid()) {
4395+
anyInvalid = true;
4396+
continue;
4397+
}
43814398

4382-
// If there is an empty witness, record it.
4383-
if (normal->hasWitness(value) &&
4384-
!normal->getWitness(value, nullptr).getDecl()) {
4385-
hasAnyUnsatisfiedOptionalReqs = true;
4386-
break;
4387-
}
4399+
// Check whether there are any unsatisfied requirements.
4400+
auto proto = conformance->getProtocol();
4401+
for (auto member : proto->getMembers()) {
4402+
auto req = dyn_cast<ValueDecl>(member);
4403+
if (!req) continue;
4404+
4405+
// If the requirement is unsatisfied, we might want to warn
4406+
// about near misses; record it.
4407+
if (isUnsatisfiedReq(normal, req)) {
4408+
unsatisfiedReqs[req->getBaseName()].push_back(req);
4409+
continue;
43884410
}
43894411
}
43904412
}
@@ -4413,50 +4435,43 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc,
44134435
diag.ExistingExplicitProtocol->getName());
44144436
}
44154437

4416-
// If there were any unsatisfied optional requirements, check whether
4417-
// there are any near-matches we should diagnose.
4418-
if (hasAnyUnsatisfiedOptionalReqs) {
4419-
// Catalog all of members of this declaration context that satisfy
4420-
// requirements of conformances in this context.
4421-
llvm::MapVector<DeclName, llvm::TinyPtrVector<ValueDecl *>>
4422-
unsatisfiedOptionalReqs;
4438+
// If there were any unsatisfied requirements, check whether there
4439+
// are any near-matches we should diagnose.
4440+
if (!unsatisfiedReqs.empty() && !anyInvalid) {
4441+
// Collect the set of witnesses that came from this context.
44234442
llvm::SmallPtrSet<ValueDecl *, 16> knownWitnesses;
44244443
for (auto conformance : conformances) {
44254444
conformance->forEachValueWitness(
44264445
nullptr,
44274446
[&](ValueDecl *req, ConcreteDeclRef witness) {
44284447
// If there is a witness, record it if it's within this context.
4429-
if (witness.getDecl()) {
4430-
if (witness.getDecl()->getDeclContext() == dc)
4431-
knownWitnesses.insert(witness.getDecl());
4432-
return;
4433-
}
4434-
4435-
// There is no witness. If this was an optional requirement,
4436-
// record it as such.
4437-
if (req->getAttrs().hasAttribute<OptionalAttr>())
4438-
unsatisfiedOptionalReqs[req->getBaseName()].push_back(req);
4448+
if (witness.getDecl() && witness.getDecl()->getDeclContext() == dc)
4449+
knownWitnesses.insert(witness.getDecl());
44394450
});
44404451
}
44414452

4442-
// Find all of the members that aren't used to satisfy requirements,
4443-
// and check whether they are close to an unsatisfied requirement.
4453+
// Find all of the members that aren't used to satisfy
4454+
// requirements, and check whether they are close to an
4455+
// unsatisfied or defaulted requirement.
44444456
for (auto member : idc->getMembers()) {
4445-
// Filter out anything that couldn't satisfy an optional requirement.
4457+
// Filter out anything that couldn't satisfy one of the
4458+
// requirements or was used to satisfy a different requirement.
44464459
auto value = dyn_cast<ValueDecl>(member);
44474460
if (!value) continue;
44484461
if (isa<TypeDecl>(value)) continue;
44494462
if (knownWitnesses.count(value) > 0) continue;
44504463
if (!value->getFullName()) continue;
44514464

4452-
// Consider any optional requirements with the same base name.
4453-
auto optionalReqs = unsatisfiedOptionalReqs.find(value->getBaseName());
4454-
if (optionalReqs == unsatisfiedOptionalReqs.end()) continue;
4465+
// Consider any unsatisfied requirements with the same base
4466+
// name.
4467+
auto reqs = unsatisfiedReqs.find(value->getBaseName());
4468+
if (reqs == unsatisfiedReqs.end()) continue;
44554469

4456-
// Find the optional requirements with the nearest-matching names.
4470+
// Find the unsatisfied requirements with the nearest-matching
4471+
// names.
44574472
SmallVector<ValueDecl *, 4> bestOptionalReqs;
44584473
unsigned bestScore = UINT_MAX;
4459-
for (auto req : optionalReqs->second) {
4474+
for (auto req : reqs->second) {
44604475
// Score this particular optional requirement.
44614476
auto score = scorePotentiallyMatchingNames(value->getFullName(),
44624477
req->getFullName(),
@@ -4496,19 +4511,20 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc,
44964511

44974512
// Remove this optional requirement from the list. We don't want to
44984513
// complain about it twice.
4499-
if (optionalReqs->second.size() == 1) {
4500-
unsatisfiedOptionalReqs.erase(optionalReqs);
4514+
if (reqs->second.size() == 1) {
4515+
unsatisfiedReqs.erase(reqs);
45014516
} else {
4502-
optionalReqs->second.erase(std::find(optionalReqs->second.begin(),
4503-
optionalReqs->second.end(),
4504-
req));
4517+
reqs->second.erase(std::find(reqs->second.begin(),
4518+
reqs->second.end(),
4519+
req));
45054520
}
45064521
}
45074522
}
45084523

4509-
// For any unsatified optional @objc requirements that remain,
4510-
// note them in the AST for @objc selector collision checking.
4511-
for (const auto &unsatisfied : unsatisfiedOptionalReqs) {
4524+
// For any unsatified optional @objc requirements that remain
4525+
// unsatisfied, note them in the AST for @objc selector collision
4526+
// checking.
4527+
for (const auto &unsatisfied : unsatisfiedReqs) {
45124528
for (auto req : unsatisfied.second) {
45134529
// Skip non-@objc requirements.
45144530
if (!req->isObjC()) continue;

0 commit comments

Comments
 (0)