Skip to content

Commit 8fff45d

Browse files
authored
Merge pull request #6770 from xedin/cgr-refactor
[QoI] Refactor TypeChecker::checkGenericArguments. NFC
2 parents 48ef1ad + 599be90 commit 8fff45d

File tree

4 files changed

+94
-81
lines changed

4 files changed

+94
-81
lines changed

lib/Sema/CSDiag.cpp

Lines changed: 19 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -5704,9 +5704,6 @@ bool FailureDiagnosis::diagnoseArgumentGenericRequirements(
57045704
/*allowFixes=*/false, listener, bindings))
57055705
return false;
57065706

5707-
auto loc = argExpr->getLoc();
5708-
auto noteLoc = AFD->getLoc();
5709-
57105707
TypeSubstitutionMap substitutions;
57115708
// First, let's collect all of the archetypes and their substitutions,
57125709
// that's going to help later on if there are cross-archetype
@@ -5740,72 +5737,32 @@ bool FailureDiagnosis::diagnoseArgumentGenericRequirements(
57405737
if (substitutions.empty())
57415738
return false;
57425739

5743-
auto dc = env->getOwningDeclContext();
5744-
auto module = dc->getParentModule();
5745-
auto signature = env->getGenericSignature();
5746-
5747-
for (const auto &req : signature->getRequirements()) {
5748-
Type firstType = req.getFirstType().subst(module, substitutions);
5749-
if (firstType.isNull())
5750-
continue;
5740+
class RequirementsListener : public GenericRequirementsCheckListener {
5741+
private:
5742+
bool DiagnosedAny = false;
57515743

5752-
Type secondType = req.getSecondType();
5753-
if (secondType) {
5754-
secondType = secondType.subst(module, substitutions);
5755-
if (secondType.isNull())
5756-
continue;
5744+
public:
5745+
bool shouldCheck(RequirementKind kind, Type first, Type second) override {
5746+
// This means that we have encountered requirement which references
5747+
// generic parameter not used in the arguments, we can't diagnose it here.
5748+
return !(first->hasTypeParameter() || first->isTypeVariableOrMember());
57575749
}
57585750

5759-
// This means that we have encountered requirement which references
5760-
// generic parameter not used in the arguments, we can't diagnose it here.
5761-
if (firstType->hasTypeParameter() || firstType->isTypeVariableOrMember())
5762-
continue;
5763-
5764-
switch (req.getKind()) {
5765-
case RequirementKind::Conformance: {
5766-
auto protocol = secondType->castTo<ProtocolType>();
5767-
auto result = TC.conformsToProtocol(
5768-
firstType, protocol->getDecl(), dc,
5769-
ConformanceCheckFlags::SuppressDependencyTracking, loc, nullptr);
5770-
5771-
// Conformance failed and was diagnosed by `conformsToProtocol`.
5772-
if (!result.second)
5773-
return true;
5774-
5775-
break;
5751+
void diagnosed(const Requirement *requirement) override {
5752+
DiagnosedAny = true;
57765753
}
57775754

5778-
case RequirementKind::Superclass:
5779-
if (!TC.isSubtypeOf(firstType, secondType, dc)) {
5780-
TC.diagnose(loc, diag::type_does_not_inherit, AFD->getInterfaceType(),
5781-
firstType, secondType);
5782-
5783-
TC.diagnose(
5784-
noteLoc, diag::type_does_not_inherit_requirement,
5785-
req.getFirstType(), req.getSecondType(),
5786-
signature->gatherGenericParamBindingsText(
5787-
{req.getFirstType(), req.getSecondType()}, substitutions));
5788-
return true;
5789-
}
5790-
break;
5791-
5792-
case RequirementKind::SameType:
5793-
if (!firstType->isEqual(secondType)) {
5794-
TC.diagnose(loc, diag::types_not_equal, AFD->getInterfaceType(),
5795-
firstType, secondType);
5755+
bool foundProblems() const { return DiagnosedAny; }
5756+
};
57965757

5797-
TC.diagnose(
5798-
noteLoc, diag::types_not_equal_requirement, req.getFirstType(),
5799-
req.getSecondType(),
5800-
signature->gatherGenericParamBindingsText(
5801-
{req.getFirstType(), req.getSecondType()}, substitutions));
5802-
return true;
5803-
}
5804-
break;
5805-
}
5806-
}
5758+
RequirementsListener genericReqListener;
5759+
TC.checkGenericArguments(env->getOwningDeclContext(), argExpr->getLoc(),
5760+
AFD->getLoc(), AFD->getInterfaceType(),
5761+
env->getGenericSignature(), substitutions, nullptr,
5762+
ConformanceCheckFlags::SuppressDependencyTracking,
5763+
&genericReqListener);
58075764

5808-
return false;
5765+
return genericReqListener.foundProblems();
58095766
}
58105767

58115768
/// When initializing Unsafe[Mutable]Pointer<T> from Unsafe[Mutable]RawPointer,

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1375,6 +1375,16 @@ Expr *ExprTypeCheckListener::appliedSolution(Solution &solution, Expr *expr) {
13751375
return expr;
13761376
}
13771377

1378+
GenericRequirementsCheckListener::~GenericRequirementsCheckListener() {}
1379+
1380+
bool GenericRequirementsCheckListener::shouldCheck(RequirementKind kind,
1381+
Type first, Type second) {
1382+
return true;
1383+
}
1384+
1385+
void GenericRequirementsCheckListener::diagnosed(
1386+
const Requirement *withRequirement) {}
1387+
13781388
bool TypeChecker::
13791389
solveForExpression(Expr *&expr, DeclContext *dc, Type convertType,
13801390
FreeTypeVariableBinding allowFreeTypeVariables,

lib/Sema/TypeCheckGeneric.cpp

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -843,13 +843,12 @@ void TypeChecker::revertGenericFuncSignature(AbstractFunctionDecl *func) {
843843
}
844844
}
845845

846-
std::pair<bool, bool>
847-
TypeChecker::checkGenericArguments(DeclContext *dc, SourceLoc loc,
848-
SourceLoc noteLoc,
849-
Type owner,
850-
GenericSignature *genericSig,
851-
const TypeSubstitutionMap &substitutions,
852-
UnsatisfiedDependency *unsatisfiedDependency) {
846+
std::pair<bool, bool> TypeChecker::checkGenericArguments(
847+
DeclContext *dc, SourceLoc loc, SourceLoc noteLoc, Type owner,
848+
GenericSignature *genericSig, const TypeSubstitutionMap &substitutions,
849+
UnsatisfiedDependency *unsatisfiedDependency,
850+
ConformanceCheckOptions conformanceOptions,
851+
GenericRequirementsCheckListener *listener) {
853852
// Check each of the requirements.
854853
ModuleDecl *module = dc->getParentModule();
855854
for (const auto &req : genericSig->getRequirements()) {
@@ -870,26 +869,33 @@ TypeChecker::checkGenericArguments(DeclContext *dc, SourceLoc loc,
870869
}
871870
}
872871

873-
switch (req.getKind()) {
872+
auto kind = req.getKind();
873+
if (listener && !listener->shouldCheck(kind, firstType, secondType))
874+
continue;
875+
876+
switch (kind) {
874877
case RequirementKind::Conformance: {
875878
// Protocol conformance requirements.
876879
auto proto = secondType->castTo<ProtocolType>();
877880
// FIXME: This should track whether this should result in a private
878881
// or non-private dependency.
879882
// FIXME: Do we really need "used" at this point?
880883
// FIXME: Poor location information. How much better can we do here?
881-
auto result = conformsToProtocol(
882-
firstType, proto->getDecl(), dc,
883-
ConformanceCheckFlags::Used, loc,
884-
unsatisfiedDependency);
884+
auto result =
885+
conformsToProtocol(firstType, proto->getDecl(), dc,
886+
conformanceOptions, loc, unsatisfiedDependency);
885887

886888
// Unsatisfied dependency case.
887889
if (result.first)
888890
return std::make_pair(true, false);
889891

890892
// Conformance check failure case.
891-
if (!result.second)
893+
if (!result.second) {
894+
if (listener && loc.isValid())
895+
listener->diagnosed(&req);
896+
892897
return std::make_pair(false, false);
898+
}
893899

894900
continue;
895901
}
@@ -912,6 +918,10 @@ TypeChecker::checkGenericArguments(DeclContext *dc, SourceLoc loc,
912918
req.getFirstType(), req.getSecondType(),
913919
genericSig->gatherGenericParamBindingsText(
914920
{req.getFirstType(), req.getSecondType()}, substitutions));
921+
922+
if (listener)
923+
listener->diagnosed(&req);
924+
915925
return std::make_pair(false, false);
916926
}
917927
continue;
@@ -925,6 +935,10 @@ TypeChecker::checkGenericArguments(DeclContext *dc, SourceLoc loc,
925935
req.getSecondType(),
926936
genericSig->gatherGenericParamBindingsText(
927937
{req.getFirstType(), req.getSecondType()}, substitutions));
938+
939+
if (listener)
940+
listener->diagnosed(&req);
941+
928942
return std::make_pair(false, false);
929943
}
930944
continue;

lib/Sema/TypeChecker.h

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,35 @@ class ExprTypeCheckListener {
331331
Expr *expr);
332332
};
333333

334+
/// An abstract interface that is used by `checkGenericArguments`.
335+
class GenericRequirementsCheckListener {
336+
public:
337+
virtual ~GenericRequirementsCheckListener();
338+
339+
/// Callback invoked before trying to check generic requirement placed
340+
/// between given types. Note: if either of the types assigned to the
341+
/// requirement is generic parameter or dependent member, this callback
342+
/// method is going to get their substitutions.
343+
///
344+
/// \param kind The kind of generic requirement to check.
345+
///
346+
/// \param first The left-hand side type assigned to the requirement,
347+
/// possibly represented by its generic substitute.
348+
///
349+
/// \param second The right-hand side type assinged to the requirement,
350+
/// possibly represented by its generic substitute.
351+
///
352+
///
353+
/// \returns true if it's ok to validate requirement, false otherwise.
354+
virtual bool shouldCheck(RequirementKind kind, Type first, Type second);
355+
356+
/// Callback invoked when given requirement has been diagnosed as invalid.
357+
///
358+
/// \param requirement The requirement which didn't pass the check.
359+
///
360+
virtual void diagnosed(const Requirement *requirement);
361+
};
362+
334363
/// Flags that describe the context of type checking a pattern or
335364
/// type.
336365
enum TypeResolutionFlags : unsigned {
@@ -1156,18 +1185,21 @@ class TypeChecker final : public LazyResolver {
11561185
/// \param substitutions Substitutions from interface types of the signature.
11571186
/// \param unsatisfiedDependency Optional callback for reporting unsatisfied
11581187
/// dependencies.
1188+
/// \param conformanceOptions The flags to use when checking conformance
1189+
/// requirement.
1190+
/// \param listener The generic check listener used to pick requirements and
1191+
/// notify callers about diagnosed errors.
11591192
///
11601193
/// \returns One of the following:
11611194
/// - (true, false) if there was an unsatisfied dependency
11621195
/// - (false, true) on success
11631196
/// - (false, false) on failure
11641197
std::pair<bool, bool> checkGenericArguments(
1165-
DeclContext *dc, SourceLoc loc,
1166-
SourceLoc noteLoc,
1167-
Type owner,
1168-
GenericSignature *genericSig,
1169-
const TypeSubstitutionMap &substitutions,
1170-
UnsatisfiedDependency *unsatisfiedDependency);
1198+
DeclContext *dc, SourceLoc loc, SourceLoc noteLoc, Type owner,
1199+
GenericSignature *genericSig, const TypeSubstitutionMap &substitutions,
1200+
UnsatisfiedDependency *unsatisfiedDependency,
1201+
ConformanceCheckOptions conformanceOptions = ConformanceCheckFlags::Used,
1202+
GenericRequirementsCheckListener *listener = nullptr);
11711203

11721204
/// Resolve the superclass of the given class.
11731205
void resolveSuperclass(ClassDecl *classDecl) override;

0 commit comments

Comments
 (0)