Skip to content

Commit 21d23c3

Browse files
authored
Merge pull request swiftlang#83247 from tshortli/refactor-requirement-check
Sema: Consolidate requirement check failures and refactor `RequirementCheck`
2 parents 027aa0c + e618669 commit 21d23c3

File tree

3 files changed

+104
-76
lines changed

3 files changed

+104
-76
lines changed

include/swift/AST/RequirementMatch.h

Lines changed: 53 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -217,18 +217,14 @@ enum class CheckKind : unsigned {
217217
/// The witness is less accessible than the requirement.
218218
Access,
219219

220-
/// The witness is storage whose setter is less accessible than the
221-
/// requirement.
222-
AccessOfSetter,
223-
224220
/// The witness needs to be @usableFromInline.
225221
UsableFromInline,
226222

227223
/// The witness is less available than the requirement.
228224
Availability,
229225

230226
/// The requirement was marked explicitly unavailable.
231-
Unavailable,
227+
RequirementUnavailable,
232228

233229
/// The witness requires optional adjustments.
234230
OptionalityConflict,
@@ -237,39 +233,71 @@ enum class CheckKind : unsigned {
237233
/// requirement.
238234
ConstructorFailability,
239235

240-
/// The witness itself is inaccessible.
241-
WitnessUnavailable,
242-
243236
/// The witness is a deprecated default implementation provided by the
244237
/// protocol.
245238
DefaultWitnessDeprecated,
246239
};
240+
247241
/// Describes the suitability of the chosen witness for
248242
/// the requirement.
249-
struct RequirementCheck {
243+
class RequirementCheck {
250244
CheckKind Kind;
251245

252-
/// The required access scope, if the check failed due to the
253-
/// witness being less accessible than the requirement.
254-
AccessScope RequiredAccessScope;
246+
union {
247+
/// Storage for `CheckKind::Access`.
248+
struct {
249+
AccessScope requiredScope;
250+
bool forSetter;
251+
} Access;
255252

256-
/// The required availability, if the check failed due to the
257-
/// witness being less available than the requirement.
258-
AvailabilityRange RequiredAvailability;
253+
/// Storage for `CheckKind::Availability`.
254+
struct {
255+
AvailabilityRange requiredRange;
256+
} Availability;
257+
};
259258

260-
RequirementCheck(CheckKind kind)
261-
: Kind(kind), RequiredAccessScope(AccessScope::getPublic()),
262-
RequiredAvailability(AvailabilityRange::alwaysAvailable()) {}
259+
public:
260+
RequirementCheck(CheckKind kind) : Kind(kind) {
261+
// These kinds have their own constructors.
262+
ASSERT(kind != CheckKind::Access);
263+
ASSERT(kind != CheckKind::Availability);
264+
}
263265

264-
RequirementCheck(CheckKind kind, AccessScope requiredAccessScope)
265-
: Kind(kind), RequiredAccessScope(requiredAccessScope),
266-
RequiredAvailability(AvailabilityRange::alwaysAvailable()) {}
266+
RequirementCheck(AccessScope requiredAccessScope, bool forSetter)
267+
: Kind(CheckKind::Access), Access{requiredAccessScope, forSetter} {}
267268

268-
RequirementCheck(CheckKind kind, AvailabilityRange requiredAvailability)
269-
: Kind(kind), RequiredAccessScope(AccessScope::getPublic()),
270-
RequiredAvailability(requiredAvailability) {}
271-
};
269+
RequirementCheck(AvailabilityRange requiredRange)
270+
: Kind(CheckKind::Availability), Availability{requiredRange} {}
272271

272+
CheckKind getKind() const { return Kind; }
273+
274+
/// True if the witness is storage whose setter is less accessible than the
275+
/// requirement.
276+
bool isForSetterAccess() const {
277+
return (Kind == CheckKind::Access) ? Access.forSetter : false;
278+
}
279+
280+
/// True if the witness is less available than the requirement.
281+
bool isLessAvailable() const {
282+
return (Kind == CheckKind::Availability)
283+
? !Availability.requiredRange.isKnownUnreachable()
284+
: false;
285+
}
286+
287+
/// The required access scope for checks that failed due to the witness being
288+
/// less accessible than the requirement.
289+
AccessScope getRequiredAccessScope() const {
290+
ASSERT(Kind == CheckKind::Access);
291+
return Access.requiredScope;
292+
}
293+
294+
/// The required availability range for checks that failed due to the witness
295+
/// being less available than the requirement.
296+
AvailabilityRange getRequiredAvailabilityRange() const {
297+
ASSERT(Kind == CheckKind::Availability);
298+
return Availability.requiredRange;
299+
}
300+
};
273301

274302
/// Describes a match between a requirement and a witness.
275303
struct RequirementMatch {

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 50 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1653,7 +1653,7 @@ bool WitnessChecker::findBestWitness(
16531653
for (auto match : matches) {
16541654
if (!match.isViable()) {
16551655
checkedMatches.push_back(match);
1656-
} else if (checkWitness(requirement, match).Kind != CheckKind::Availability) {
1656+
} else if (!checkWitness(requirement, match).isLessAvailable()) {
16571657
foundCheckedMatch = true;
16581658
checkedMatches.push_back(match);
16591659
}
@@ -1835,20 +1835,15 @@ RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement,
18351835
if (!match.OptionalAdjustments.empty())
18361836
return CheckKind::OptionalityConflict;
18371837

1838-
auto requiredAccessScope = evaluateOrDefault(
1838+
auto [requiredAccessLevel, mustBeUsableFromInline] = evaluateOrDefault(
18391839
Context.evaluator, ConformanceAccessScopeRequest{DC, Proto},
18401840
std::make_pair(AccessScope::getPublic(), false));
18411841

18421842
bool isSetter = false;
1843-
if (checkWitnessAccess(DC, requirement, match.Witness, &isSetter)) {
1844-
CheckKind kind = (isSetter
1845-
? CheckKind::AccessOfSetter
1846-
: CheckKind::Access);
1847-
1848-
return RequirementCheck(kind, requiredAccessScope.first);
1849-
}
1843+
if (checkWitnessAccess(DC, requirement, match.Witness, &isSetter))
1844+
return RequirementCheck(requiredAccessLevel, isSetter);
18501845

1851-
if (requiredAccessScope.second) {
1846+
if (mustBeUsableFromInline) {
18521847
bool witnessIsUsableFromInline = match.Witness->getFormalAccessScope(
18531848
DC, /*usableFromInlineAsPublic*/true).isPublic();
18541849
if (!witnessIsUsableFromInline)
@@ -1858,11 +1853,11 @@ RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement,
18581853
auto requiredAvailability = AvailabilityRange::alwaysAvailable();
18591854
if (checkWitnessAvailability(requirement, match.Witness,
18601855
&requiredAvailability)) {
1861-
return RequirementCheck(CheckKind::Availability, requiredAvailability);
1856+
return RequirementCheck(requiredAvailability);
18621857
}
18631858

18641859
if (requirement->isUnavailable() && match.Witness->getDeclContext() == DC) {
1865-
return RequirementCheck(CheckKind::Unavailable);
1860+
return RequirementCheck(CheckKind::RequirementUnavailable);
18661861
}
18671862

18681863
// A non-failable initializer requirement cannot be satisfied
@@ -1901,7 +1896,7 @@ RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement,
19011896

19021897
// Allow unavailable nominals or extension to have unavailable witnesses.
19031898
if (!nominalOrExtensionIsUnavailable())
1904-
return CheckKind::WitnessUnavailable;
1899+
return RequirementCheck(AvailabilityRange::neverAvailable());
19051900
}
19061901

19071902
// Warn about deprecated default implementations if the requirement is
@@ -4412,12 +4407,11 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
44124407

44134408
auto check = checkWitness(requirement, best);
44144409

4415-
switch (check.Kind) {
4410+
switch (check.getKind()) {
44164411
case CheckKind::Success:
44174412
break;
44184413

4419-
case CheckKind::Access:
4420-
case CheckKind::AccessOfSetter: {
4414+
case CheckKind::Access: {
44214415
// Swift 4.2 relaxed some rules for protocol witness matching.
44224416
//
44234417
// This meant that it was possible for an optional protocol requirement
@@ -4438,7 +4432,7 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
44384432
getASTContext().addDelayedConformanceDiag(Conformance, false,
44394433
[DC, witness, check, requirement](
44404434
NormalProtocolConformance *conformance) {
4441-
auto requiredAccessScope = check.RequiredAccessScope;
4435+
auto requiredAccessScope = check.getRequiredAccessScope();
44424436
AccessLevel requiredAccess =
44434437
requiredAccessScope.requiredAccessForDiagnostics();
44444438
auto proto = conformance->getProtocol();
@@ -4448,7 +4442,7 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
44484442
auto diagKind = protoForcesAccess
44494443
? diag::witness_not_accessible_proto
44504444
: diag::witness_not_accessible_type;
4451-
bool isSetter = (check.Kind == CheckKind::AccessOfSetter);
4445+
bool isSetter = check.isForSetterAccess();
44524446

44534447
auto &diags = DC->getASTContext().Diags;
44544448
diags.diagnose(getLocForDiagnosingWitness(conformance, witness),
@@ -4473,25 +4467,46 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
44734467
break;
44744468

44754469
case CheckKind::Availability: {
4476-
getASTContext().addDelayedConformanceDiag(Conformance, false,
4477-
[witness, requirement, check](
4478-
NormalProtocolConformance *conformance) {
4479-
// FIXME: The problem may not be the OS version.
4480-
ASTContext &ctx = witness->getASTContext();
4481-
auto &diags = ctx.Diags;
4482-
SourceLoc diagLoc = getLocForDiagnosingWitness(conformance, witness);
4483-
diags.diagnose(diagLoc, diag::availability_protocol_requires_version,
4484-
conformance->getProtocol(), witness,
4485-
ctx.getTargetAvailabilityDomain(),
4486-
check.RequiredAvailability);
4487-
emitDeclaredHereIfNeeded(diags, diagLoc, witness);
4488-
diags.diagnose(requirement,
4489-
diag::availability_protocol_requirement_here);
4490-
});
4470+
if (check.isLessAvailable()) {
4471+
ASSERT(check.getRequiredAvailabilityRange().hasMinimumVersion());
4472+
getASTContext().addDelayedConformanceDiag(
4473+
Conformance, false,
4474+
[witness, requirement,
4475+
check](NormalProtocolConformance *conformance) {
4476+
ASTContext &ctx = witness->getASTContext();
4477+
auto &diags = ctx.Diags;
4478+
SourceLoc diagLoc =
4479+
getLocForDiagnosingWitness(conformance, witness);
4480+
diags.diagnose(diagLoc,
4481+
diag::availability_protocol_requires_version,
4482+
conformance->getProtocol(), witness,
4483+
ctx.getTargetAvailabilityDomain(),
4484+
check.getRequiredAvailabilityRange());
4485+
emitDeclaredHereIfNeeded(diags, diagLoc, witness);
4486+
diags.diagnose(requirement,
4487+
diag::availability_protocol_requirement_here);
4488+
});
4489+
} else {
4490+
getASTContext().addDelayedConformanceDiag(
4491+
Conformance, true,
4492+
[witness, requirement](NormalProtocolConformance *conformance) {
4493+
auto &diags = witness->getASTContext().Diags;
4494+
auto diagLoc = getLocForDiagnosingWitness(conformance, witness);
4495+
// FIXME: [availability] Get the original constraint.
4496+
auto attr = witness->getUnavailableAttr();
4497+
EncodedDiagnosticMessage EncodedMessage(attr->getMessage());
4498+
diags.diagnose(diagLoc, diag::witness_unavailable, witness,
4499+
conformance->getProtocol(),
4500+
EncodedMessage.Message);
4501+
emitDeclaredHereIfNeeded(diags, diagLoc, witness);
4502+
diags.diagnose(requirement, diag::requirement_declared_here,
4503+
requirement);
4504+
});
4505+
}
44914506
break;
44924507
}
44934508

4494-
case CheckKind::Unavailable: {
4509+
case CheckKind::RequirementUnavailable: {
44954510
diagnoseOverrideOfUnavailableDecl(
44964511
witness, requirement, requirement->getUnavailableAttr().value());
44974512
break;
@@ -4544,21 +4559,6 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
45444559

45454560
break;
45464561

4547-
case CheckKind::WitnessUnavailable:
4548-
getASTContext().addDelayedConformanceDiag(Conformance, true,
4549-
[witness, requirement](NormalProtocolConformance *conformance) {
4550-
auto &diags = witness->getASTContext().Diags;
4551-
SourceLoc diagLoc = getLocForDiagnosingWitness(conformance, witness);
4552-
auto attr = witness->getUnavailableAttr();
4553-
EncodedDiagnosticMessage EncodedMessage(attr->getMessage());
4554-
diags.diagnose(diagLoc, diag::witness_unavailable, witness,
4555-
conformance->getProtocol(), EncodedMessage.Message);
4556-
emitDeclaredHereIfNeeded(diags, diagLoc, witness);
4557-
diags.diagnose(requirement, diag::requirement_declared_here,
4558-
requirement);
4559-
});
4560-
break;
4561-
45624562
case CheckKind::DefaultWitnessDeprecated:
45634563
getASTContext().addDelayedConformanceDiag(
45644564
Conformance, /*isError=*/false,
@@ -7213,7 +7213,7 @@ DefaultWitnessChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
72137213
// Perform the same checks as conformance witness matching, but silently
72147214
// ignore the candidate instead of diagnosing anything.
72157215
auto check = checkWitness(requirement, best);
7216-
if (check.Kind != CheckKind::Success)
7216+
if (check.getKind() != CheckKind::Success)
72177217
return ResolveWitnessResult::ExplicitFailed;
72187218

72197219
// Record the match.

lib/Sema/TypeCheckProtocol.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ SmallVector<ValueDecl *, 4> lookupValueWitnesses(DeclContext *DC,
5353
ValueDecl *req,
5454
bool *ignoringNames);
5555

56-
struct RequirementCheck;
56+
class RequirementCheck;
5757

5858
class WitnessChecker {
5959
public:

0 commit comments

Comments
 (0)