Skip to content

Commit a74717a

Browse files
committed
[RequirementMachine] Diagnose non-protocol, non-class conformance/subtype
constraints in the requirement machine.
1 parent 1665c57 commit a74717a

File tree

5 files changed

+132
-15
lines changed

5 files changed

+132
-15
lines changed

include/swift/AST/Requirement.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,40 @@ struct StructuralRequirement {
124124
bool inferred = false;
125125
};
126126

127+
/// Represents an invalid requirement, such as `T: Int`.
128+
///
129+
/// Invalid requirements are recorded while computing the
130+
/// generic signature of a declaration, and diagnosed via
131+
/// \c diagnoseRequirementErrors .
132+
struct RequirementError {
133+
enum class Kind {
134+
InvalidConformance,
135+
} kind;
136+
137+
union {
138+
struct {
139+
Type subjectType;
140+
Type constraint;
141+
} invalidConformance;
142+
};
143+
144+
SourceLoc loc;
145+
146+
private:
147+
RequirementError(Kind kind, SourceLoc loc)
148+
: kind(kind), loc(loc) {}
149+
150+
public:
151+
static RequirementError forInvalidConformance(Type subjectType,
152+
Type constraint,
153+
SourceLoc loc) {
154+
RequirementError error(Kind::InvalidConformance, loc);
155+
error.invalidConformance.subjectType = subjectType;
156+
error.invalidConformance.constraint = constraint;
157+
return error;
158+
}
159+
};
160+
127161
} // end namespace swift
128162

129163
#endif

lib/AST/RequirementMachine/RequirementLowering.cpp

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,8 @@ swift::rewriting::desugarRequirement(Requirement req,
229229

230230
static void realizeTypeRequirement(Type subjectType, Type constraintType,
231231
SourceLoc loc,
232-
SmallVectorImpl<StructuralRequirement> &result) {
232+
SmallVectorImpl<StructuralRequirement> &result,
233+
SmallVectorImpl<RequirementError> &errors) {
233234
SmallVector<Requirement, 2> reqs;
234235

235236
if (constraintType->isConstraintType()) {
@@ -239,7 +240,10 @@ static void realizeTypeRequirement(Type subjectType, Type constraintType,
239240
// Handle superclass requirements.
240241
desugarSuperclassRequirement(subjectType, constraintType, reqs);
241242
} else {
242-
// FIXME: Diagnose
243+
errors.push_back(
244+
RequirementError::forInvalidConformance(subjectType,
245+
constraintType,
246+
loc));
243247
return;
244248
}
245249

@@ -373,7 +377,8 @@ void swift::rewriting::inferRequirements(
373377
void swift::rewriting::realizeRequirement(
374378
Requirement req, RequirementRepr *reqRepr,
375379
ModuleDecl *moduleForInference,
376-
SmallVectorImpl<StructuralRequirement> &result) {
380+
SmallVectorImpl<StructuralRequirement> &result,
381+
SmallVectorImpl<RequirementError> &errors) {
377382
auto firstType = req.getFirstType();
378383
auto loc = (reqRepr ? reqRepr->getSeparatorLoc() : SourceLoc());
379384

@@ -391,7 +396,7 @@ void swift::rewriting::realizeRequirement(
391396
inferRequirements(secondType, secondLoc, moduleForInference, result);
392397
}
393398

394-
realizeTypeRequirement(firstType, secondType, loc, result);
399+
realizeTypeRequirement(firstType, secondType, loc, result, errors);
395400
break;
396401
}
397402

@@ -437,7 +442,8 @@ void swift::rewriting::realizeRequirement(
437442
/// AssociatedTypeDecl or GenericTypeParamDecl.
438443
void swift::rewriting::realizeInheritedRequirements(
439444
TypeDecl *decl, Type type, ModuleDecl *moduleForInference,
440-
SmallVectorImpl<StructuralRequirement> &result) {
445+
SmallVectorImpl<StructuralRequirement> &result,
446+
SmallVectorImpl<RequirementError> &errors) {
441447
auto &ctx = decl->getASTContext();
442448
auto inheritedTypes = decl->getInherited();
443449

@@ -455,7 +461,57 @@ void swift::rewriting::realizeInheritedRequirements(
455461
inferRequirements(inheritedType, loc, moduleForInference, result);
456462
}
457463

458-
realizeTypeRequirement(type, inheritedType, loc, result);
464+
realizeTypeRequirement(type, inheritedType, loc, result, errors);
465+
}
466+
}
467+
468+
void swift::rewriting::diagnoseRequirementErrors(
469+
ASTContext &ctx, SmallVectorImpl<RequirementError> &errors,
470+
bool allowConcreteGenericParams) {
471+
if (ctx.LangOpts.RequirementMachineProtocolSignatures !=
472+
RequirementMachineMode::Enabled)
473+
return;
474+
475+
for (auto error : errors) {
476+
SourceLoc loc = error.loc;
477+
478+
switch (error.kind) {
479+
case RequirementError::Kind::InvalidConformance: {
480+
Type subjectType = error.invalidConformance.subjectType;
481+
Type constraint = error.invalidConformance.constraint;
482+
483+
// FIXME: The constraint string is printed directly here because
484+
// the current default is to not print `any` for existential
485+
// types, but this error message is super confusing without `any`
486+
// if the user wrote it explicitly.
487+
PrintOptions options;
488+
options.PrintExplicitAny = true;
489+
auto constraintString = constraint.getString(options);
490+
491+
ctx.Diags.diagnose(loc, diag::requires_conformance_nonprotocol,
492+
subjectType, constraintString);
493+
494+
auto getNameWithoutSelf = [&](std::string subjectTypeName) {
495+
std::string selfSubstring = "Self.";
496+
497+
if (subjectTypeName.rfind(selfSubstring, 0) == 0) {
498+
return subjectTypeName.erase(0, selfSubstring.length());
499+
}
500+
501+
return subjectTypeName;
502+
};
503+
504+
if (allowConcreteGenericParams) {
505+
auto subjectTypeName = subjectType.getString();
506+
auto subjectTypeNameWithoutSelf = getNameWithoutSelf(subjectTypeName);
507+
ctx.Diags.diagnose(loc, diag::requires_conformance_nonprotocol_fixit,
508+
subjectTypeNameWithoutSelf, constraintString)
509+
.fixItReplace(loc, " == ");
510+
}
511+
512+
break;
513+
}
514+
}
459515
}
460516
}
461517

@@ -465,19 +521,22 @@ StructuralRequirementsRequest::evaluate(Evaluator &evaluator,
465521
assert(!proto->hasLazyRequirementSignature());
466522

467523
SmallVector<StructuralRequirement, 4> result;
524+
SmallVector<RequirementError, 4> errors;
468525

469526
auto &ctx = proto->getASTContext();
470527

471528
auto selfTy = proto->getSelfInterfaceType();
472529

473530
realizeInheritedRequirements(proto, selfTy,
474-
/*moduleForInference=*/nullptr, result);
531+
/*moduleForInference=*/nullptr,
532+
result, errors);
475533

476534
// Add requirements from the protocol's own 'where' clause.
477535
WhereClauseOwner(proto).visitRequirements(TypeResolutionStage::Structural,
478536
[&](const Requirement &req, RequirementRepr *reqRepr) {
479537
realizeRequirement(req, reqRepr,
480-
/*moduleForInference=*/nullptr, result);
538+
/*moduleForInference=*/nullptr,
539+
result, errors);
481540
return false;
482541
});
483542

@@ -502,15 +561,15 @@ StructuralRequirementsRequest::evaluate(Evaluator &evaluator,
502561
auto assocType = assocTypeDecl->getDeclaredInterfaceType();
503562
realizeInheritedRequirements(assocTypeDecl, assocType,
504563
/*moduleForInference=*/nullptr,
505-
result);
564+
result, errors);
506565

507566
// Add requirements from this associated type's where clause.
508567
WhereClauseOwner(assocTypeDecl).visitRequirements(
509568
TypeResolutionStage::Structural,
510569
[&](const Requirement &req, RequirementRepr *reqRepr) {
511570
realizeRequirement(req, reqRepr,
512571
/*moduleForInference=*/nullptr,
513-
result);
572+
result, errors);
514573
return false;
515574
});
516575
}

lib/AST/RequirementMachine/RequirementLowering.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,17 @@ void inferRequirements(Type type, SourceLoc loc, ModuleDecl *module,
4747

4848
void realizeRequirement(Requirement req, RequirementRepr *reqRepr,
4949
ModuleDecl *moduleForInference,
50-
SmallVectorImpl<StructuralRequirement> &result);
50+
SmallVectorImpl<StructuralRequirement> &result,
51+
SmallVectorImpl<RequirementError> &errors);
5152

5253
void realizeInheritedRequirements(TypeDecl *decl, Type type,
5354
ModuleDecl *moduleForInference,
54-
SmallVectorImpl<StructuralRequirement> &result);
55+
SmallVectorImpl<StructuralRequirement> &result,
56+
SmallVectorImpl<RequirementError> &errors);
57+
58+
void diagnoseRequirementErrors(ASTContext &ctx,
59+
SmallVectorImpl<RequirementError> &errors,
60+
bool allowConcreteGenericParams);
5561

5662
std::pair<MutableTerm, MutableTerm>
5763
getRuleForRequirement(const Requirement &req,

lib/AST/RequirementMachine/RequirementMachineRequests.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -524,12 +524,13 @@ InferredGenericSignatureRequestRQM::evaluate(
524524
parentSig.getGenericParams().end());
525525

526526
SmallVector<StructuralRequirement, 4> requirements;
527+
SmallVector<RequirementError, 4> errors;
527528
for (const auto &req : parentSig.getRequirements())
528529
requirements.push_back({req, SourceLoc(), /*wasInferred=*/false});
529530

530531
const auto visitRequirement = [&](const Requirement &req,
531532
RequirementRepr *reqRepr) {
532-
realizeRequirement(req, reqRepr, parentModule, requirements);
533+
realizeRequirement(req, reqRepr, parentModule, requirements, errors);
533534
return false;
534535
};
535536

@@ -561,7 +562,7 @@ InferredGenericSignatureRequestRQM::evaluate(
561562
genericParams.push_back(gpType);
562563

563564
realizeInheritedRequirements(gpDecl, gpType, parentModule,
564-
requirements);
565+
requirements, errors);
565566
}
566567

567568
auto *lookupDC = (*gpList->begin())->getDeclContext();
@@ -625,7 +626,9 @@ InferredGenericSignatureRequestRQM::evaluate(
625626
machine->computeMinimalGenericSignatureRequirements();
626627

627628
auto result = GenericSignature::get(genericParams, minimalRequirements);
628-
bool hadError = machine->hadError();
629+
bool hadError = machine->hadError() || !errors.empty();
630+
631+
diagnoseRequirementErrors(ctx, errors, allowConcreteGenericParams);
629632

630633
// FIXME: Handle allowConcreteGenericParams
631634

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %target-typecheck-verify-swift -requirement-machine-protocol-signatures=on -requirement-machine-inferred-signatures=on
2+
3+
func testInvalidConformance() {
4+
// expected-error@+1 {{type 'T' constrained to non-protocol, non-class type 'Int'}}
5+
func invalidIntConformance<T>(_: T) where T: Int {}
6+
7+
// expected-error@+1 {{type 'T' constrained to non-protocol, non-class type 'Int'}}
8+
struct InvalidIntConformance<T: Int> {}
9+
10+
struct S<T> {
11+
// expected-error@+2 {{type 'T' constrained to non-protocol, non-class type 'Int'}}
12+
// expected-note@+1 {{use 'T == Int' to require 'T' to be 'Int'}}
13+
func method() where T: Int {}
14+
}
15+
}

0 commit comments

Comments
 (0)