Skip to content

Commit 16d204b

Browse files
authored
Merge pull request #42133 from slavapestov/rqm-minor-diagnostics-fixes
RequirementMachine: Minor diagnostics fixes
2 parents 7f80abe + 2b766c2 commit 16d204b

27 files changed

+225
-134
lines changed

lib/AST/RequirementMachine/Debug.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ enum class DebugFlags : unsigned {
7272

7373
/// Print a trace of requirement machines constructed and how long each took.
7474
Timers = (1<<16),
75+
76+
/// Print conflicting rules.
77+
ConflictingRules = (1<<17),
7578
};
7679

7780
using DebugOptions = OptionSet<DebugFlags>;

lib/AST/RequirementMachine/HomotopyReduction.cpp

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -167,30 +167,6 @@ void RewriteSystem::propagateRedundantRequirementIDs() {
167167
}
168168
}
169169

170-
/// Process pairs of conflicting rules, marking the more specific rule as
171-
/// conflicting, which instructs minimization to drop this rule.
172-
void RewriteSystem::processConflicts() {
173-
for (auto pair : ConflictingRules) {
174-
auto *existingRule = &getRule(pair.first);
175-
auto *newRule = &getRule(pair.second);
176-
177-
// The identity conformance rule ([P].[P] => [P]) will conflict with
178-
// a concrete type requirement in an invalid protocol declaration
179-
// where 'Self' is constrained to a type that does not conform to
180-
// the protocol. This rule is permanent, so don't mark it as
181-
// conflicting in this case.
182-
183-
if (!existingRule->isIdentityConformanceRule() &&
184-
existingRule->getRHS().size() >= newRule->getRHS().size())
185-
existingRule->markConflicting();
186-
if (!newRule->isIdentityConformanceRule() &&
187-
newRule->getRHS().size() >= existingRule->getRHS().size())
188-
newRule->markConflicting();
189-
190-
// FIXME: Diagnose the conflict later.
191-
}
192-
}
193-
194170
/// Find a rule to delete by looking through all loops for rewrite rules appearing
195171
/// once in empty context. Returns a pair consisting of a loop ID and a rule ID,
196172
/// otherwise returns None.
@@ -477,7 +453,6 @@ void RewriteSystem::minimizeRewriteSystem() {
477453
Minimized = 1;
478454

479455
propagateExplicitBits();
480-
processConflicts();
481456

482457
if (Context.getASTContext().LangOpts.EnableRequirementMachineLoopNormalization) {
483458
for (auto &loop : Loops) {

lib/AST/RequirementMachine/PropertyUnification.cpp

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,37 @@ static void recordRelation(Term key,
119119
(void) system.addRule(lhs, rhs, &path);
120120
}
121121

122+
/// Given two property rules that conflict because no concrete type
123+
/// can satisfy both, mark one or both rules conflicting.
124+
///
125+
/// The right hand side of one rule must be a suffix of the other
126+
/// (in which case the longer of the two rules is conflicting) or
127+
/// the right hand sides are equal (in which case both will be
128+
/// conflicting).
122129
void RewriteSystem::recordConflict(unsigned existingRuleID,
123130
unsigned newRuleID) {
124131
ConflictingRules.emplace_back(existingRuleID, newRuleID);
132+
133+
auto &existingRule = getRule(existingRuleID);
134+
auto &newRule = getRule(newRuleID);
135+
136+
if (Debug.contains(DebugFlags::ConflictingRules)) {
137+
llvm::dbgs() << "Conflicting rules:\n";
138+
llvm::dbgs() << "- " << existingRule << "\n";
139+
llvm::dbgs() << "- " << newRule << "\n";
140+
}
141+
142+
// The identity conformance rule ([P].[P] => [P]) will conflict with
143+
// a concrete type requirement in an invalid protocol declaration
144+
// where 'Self' is constrained to a type that does not conform to
145+
// the protocol. This rule is permanent, so don't mark it as
146+
// conflicting in this case.
147+
if (!existingRule.isIdentityConformanceRule() &&
148+
existingRule.getRHS().size() >= newRule.getRHS().size())
149+
existingRule.markConflicting();
150+
if (!newRule.isIdentityConformanceRule() &&
151+
newRule.getRHS().size() >= existingRule.getRHS().size())
152+
newRule.markConflicting();
125153
}
126154

127155
void PropertyMap::addConformanceProperty(
@@ -350,8 +378,10 @@ void PropertyMap::addSuperclassProperty(
350378
}
351379

352380
auto &req = props->Superclasses[props->SuperclassDecl];
353-
for (const auto &pair : req.SuperclassRules)
354-
System.recordConflict(pair.second, ruleID);
381+
for (const auto &pair : req.SuperclassRules) {
382+
if (checkRulePairOnce(pair.second, ruleID))
383+
System.recordConflict(pair.second, ruleID);
384+
}
355385
}
356386
}
357387

lib/AST/RequirementMachine/RequirementLowering.cpp

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -654,20 +654,35 @@ void swift::rewriting::realizeInheritedRequirements(
654654
}
655655
}
656656

657+
static bool shouldSuggestConcreteTypeFixit(
658+
Type type, AllowConcreteTypePolicy concreteTypePolicy) {
659+
switch (concreteTypePolicy) {
660+
case AllowConcreteTypePolicy::All:
661+
return true;
662+
663+
case AllowConcreteTypePolicy::AssocTypes:
664+
return type->is<DependentMemberType>();
665+
666+
case AllowConcreteTypePolicy::NestedAssocTypes:
667+
if (auto *memberType = type->getAs<DependentMemberType>())
668+
return memberType->getBase()->is<DependentMemberType>();
669+
670+
return false;
671+
}
672+
}
673+
657674
/// Emit diagnostics for the given \c RequirementErrors.
658675
///
659676
/// \param ctx The AST context in which to emit diagnostics.
660677
/// \param errors The set of requirement diagnostics to be emitted.
661-
/// \param allowConcreteGenericParams Whether concrete type parameters
662-
/// are permitted in the generic signature. If true, diagnostics will
663-
/// offer fix-its to turn invalid type requirements, e.g. T: Int, into
664-
/// same-type requirements.
678+
/// \param concreteTypePolicy Whether fix-its should be offered to turn
679+
/// invalid type requirements, e.g. T: Int, into same-type requirements.
665680
///
666681
/// \returns true if any errors were emitted, and false otherwise (including
667682
/// when only warnings were emitted).
668683
bool swift::rewriting::diagnoseRequirementErrors(
669684
ASTContext &ctx, ArrayRef<RequirementError> errors,
670-
bool allowConcreteGenericParams) {
685+
AllowConcreteTypePolicy concreteTypePolicy) {
671686
bool diagnosedError = false;
672687

673688
for (auto error : errors) {
@@ -697,7 +712,7 @@ bool swift::rewriting::diagnoseRequirementErrors(
697712
return subjectTypeName;
698713
};
699714

700-
if (allowConcreteGenericParams) {
715+
if (shouldSuggestConcreteTypeFixit(subjectType, concreteTypePolicy)) {
701716
auto options = PrintOptions::forDiagnosticArguments();
702717
auto subjectTypeName = subjectType.getString(options);
703718
auto subjectTypeNameWithoutSelf = getNameWithoutSelf(subjectTypeName);
@@ -725,16 +740,25 @@ bool swift::rewriting::diagnoseRequirementErrors(
725740
auto requirement = error.requirement;
726741
auto conflict = error.conflictingRequirement;
727742

743+
if (requirement.getFirstType()->hasError() ||
744+
(requirement.getKind() != RequirementKind::Layout &&
745+
requirement.getSecondType()->hasError())) {
746+
// Don't emit a cascading error.
747+
break;
748+
}
749+
728750
if (!conflict) {
729-
if (requirement.getFirstType()->hasError() ||
730-
requirement.getSecondType()->hasError()) {
731-
// Don't emit a cascading error.
732-
break;
733-
}
734751
ctx.Diags.diagnose(loc, diag::requires_same_concrete_type,
735752
requirement.getFirstType(),
736753
requirement.getSecondType());
737754
} else {
755+
if (conflict->getFirstType()->hasError() ||
756+
(conflict->getKind() != RequirementKind::Layout &&
757+
conflict->getSecondType()->hasError())) {
758+
// Don't emit a cascading error.
759+
break;
760+
}
761+
738762
auto options = PrintOptions::forDiagnosticArguments();
739763
std::string requirements;
740764
llvm::raw_string_ostream OS(requirements);
@@ -754,6 +778,13 @@ bool swift::rewriting::diagnoseRequirementErrors(
754778

755779
case RequirementError::Kind::RedundantRequirement: {
756780
auto requirement = error.requirement;
781+
if (requirement.getFirstType()->hasError() ||
782+
(requirement.getKind() != RequirementKind::Layout &&
783+
requirement.getSecondType()->hasError())) {
784+
// Don't emit a cascading error.
785+
break;
786+
}
787+
757788
switch (requirement.getKind()) {
758789
case RequirementKind::SameType:
759790
ctx.Diags.diagnose(loc, diag::redundant_same_type_to_concrete,
@@ -886,7 +917,8 @@ StructuralRequirementsRequest::evaluate(Evaluator &evaluator,
886917

887918
if (ctx.LangOpts.RequirementMachineProtocolSignatures ==
888919
RequirementMachineMode::Enabled) {
889-
diagnoseRequirementErrors(ctx, errors, /*allowConcreteGenericParams=*/false);
920+
diagnoseRequirementErrors(ctx, errors,
921+
AllowConcreteTypePolicy::NestedAssocTypes);
890922
}
891923

892924
return ctx.AllocateCopy(result);
@@ -1159,7 +1191,8 @@ TypeAliasRequirementsRequest::evaluate(Evaluator &evaluator,
11591191

11601192
if (ctx.LangOpts.RequirementMachineProtocolSignatures ==
11611193
RequirementMachineMode::Enabled) {
1162-
diagnoseRequirementErrors(ctx, errors, /*allowConcreteGenericParams=*/false);
1194+
diagnoseRequirementErrors(ctx, errors,
1195+
AllowConcreteTypePolicy::NestedAssocTypes);
11631196
}
11641197

11651198
return ctx.AllocateCopy(result);

lib/AST/RequirementMachine/RequirementLowering.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,24 @@ void realizeInheritedRequirements(TypeDecl *decl, Type type,
5555
SmallVectorImpl<StructuralRequirement> &result,
5656
SmallVectorImpl<RequirementError> &errors);
5757

58+
/// Policy for the fixit that transforms 'T : S' where 'S' is not a protocol
59+
/// or a class into 'T == S'.
60+
enum AllowConcreteTypePolicy {
61+
/// Any type parameter can be concrete.
62+
All,
63+
64+
/// Only associated types can be concrete.
65+
AssocTypes,
66+
67+
/// Only nested associated types can be concrete. This is for protocols,
68+
/// where we don't want to suggest making an associated type member of
69+
/// 'Self' concrete.
70+
NestedAssocTypes
71+
};
72+
5873
bool diagnoseRequirementErrors(ASTContext &ctx,
5974
ArrayRef<RequirementError> errors,
60-
bool allowConcreteGenericParams);
75+
AllowConcreteTypePolicy concreteTypePolicy);
6176

6277
// Defined in ConcreteContraction.cpp.
6378
bool performConcreteContraction(

lib/AST/RequirementMachine/RequirementMachineRequests.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ RequirementSignatureRequestRQM::evaluate(Evaluator &evaluator,
412412
SmallVector<RequirementError, 4> errors;
413413
machine->computeRequirementDiagnostics(errors, proto->getLoc());
414414
diagnoseRequirementErrors(ctx, errors,
415-
/*allowConcreteGenericParams=*/false);
415+
AllowConcreteTypePolicy::NestedAssocTypes);
416416
}
417417

418418
if (!machine->getErrors()) {
@@ -844,7 +844,10 @@ InferredGenericSignatureRequestRQM::evaluate(
844844
ctx.LangOpts.RequirementMachineInferredSignatures ==
845845
RequirementMachineMode::Enabled) {
846846
machine->computeRequirementDiagnostics(errors, loc);
847-
diagnoseRequirementErrors(ctx, errors, allowConcreteGenericParams);
847+
diagnoseRequirementErrors(ctx, errors,
848+
allowConcreteGenericParams
849+
? AllowConcreteTypePolicy::All
850+
: AllowConcreteTypePolicy::AssocTypes);
848851
}
849852

850853
// FIXME: Handle allowConcreteGenericParams

lib/AST/RequirementMachine/RewriteContext.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ static DebugOptions parseDebugFlags(StringRef debugFlags) {
177177
.Case("concrete-contraction", DebugFlags::ConcreteContraction)
178178
.Case("propagate-requirement-ids", DebugFlags::PropagateRequirementIDs)
179179
.Case("timers", DebugFlags::Timers)
180+
.Case("conflicting-rules", DebugFlags::ConflictingRules)
180181
.Default(None);
181182
if (!flag) {
182183
llvm::errs() << "Unknown debug flag in -debug-requirement-machine "

lib/AST/RequirementMachine/RewriteSystem.cpp

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,41 @@ void RewriteSystem::verifyRewriteRules(ValidityPolicy policy) const {
608608
#undef ASSERT_RULE
609609
}
610610

611+
/// Determine whether this is a redundantly inheritable Objective-C protocol.
612+
///
613+
/// A redundantly-inheritable Objective-C protocol is one where we will
614+
/// silently accept a directly-stated redundant conformance to this protocol,
615+
/// and emit this protocol in the list of "inherited" protocols. There are
616+
/// two cases where we allow this:
617+
///
618+
// 1) For a protocol defined in Objective-C, so that we will match Clang's
619+
/// behavior, and
620+
/// 2) For an @objc protocol defined in Swift that directly inherits from
621+
/// JavaScriptCore's JSExport, which depends on this behavior.
622+
static bool isRedundantlyInheritableObjCProtocol(const ProtocolDecl *inheritingProto,
623+
const ProtocolDecl *proto) {
624+
if (!proto->isObjC()) return false;
625+
626+
// Check the two conditions in which we will suppress the diagnostic and
627+
// emit the redundant inheritance.
628+
if (!inheritingProto->hasClangNode() && !proto->getName().is("JSExport"))
629+
return false;
630+
631+
// If the inheriting protocol already has @_restatedObjCConformance with
632+
// this protocol, we're done.
633+
for (auto *attr : inheritingProto->getAttrs()
634+
.getAttributes<RestatedObjCConformanceAttr>()) {
635+
if (attr->Proto == proto) return true;
636+
}
637+
638+
// Otherwise, add @_restatedObjCConformance.
639+
auto &ctx = proto->getASTContext();
640+
const_cast<ProtocolDecl *>(inheritingProto)
641+
->getAttrs().add(new (ctx) RestatedObjCConformanceAttr(
642+
const_cast<ProtocolDecl *>(proto)));
643+
return true;
644+
}
645+
611646
/// Computes the set of explicit redundant requirements to
612647
/// emit warnings for in the source code.
613648
void RewriteSystem::computeRedundantRequirementDiagnostics(
@@ -689,8 +724,18 @@ void RewriteSystem::computeRedundantRequirementDiagnostics(
689724
auto isRedundantRule = [&](unsigned ruleID) {
690725
const auto &rule = getRules()[ruleID];
691726

692-
return (rule.isRedundant() &&
693-
nonExplicitNonRedundantRules.count(ruleID) == 0);
727+
if (!rule.isRedundant())
728+
return false;
729+
730+
if (nonExplicitNonRedundantRules.count(ruleID) > 0)
731+
return false;
732+
733+
if (rule.isProtocolRefinementRule(Context) &&
734+
isRedundantlyInheritableObjCProtocol(rule.getLHS()[0].getProtocol(),
735+
rule.getLHS()[1].getProtocol()))
736+
return false;
737+
738+
return true;
694739
};
695740

696741
// Finally walk through the written requirements, diagnosing any that are

lib/AST/RequirementMachine/RewriteSystem.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -361,8 +361,6 @@ class RewriteSystem final {
361361

362362
void propagateRedundantRequirementIDs();
363363

364-
void processConflicts();
365-
366364
using EliminationPredicate = llvm::function_ref<bool(unsigned loopID,
367365
unsigned ruleID)>;
368366

lib/AST/RequirementMachine/Rule.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,9 +198,11 @@ class Rule final {
198198
}
199199

200200
void markConflicting() {
201+
// It's okay to mark a rule as conflicting multiple times.
202+
if (Conflicting)
203+
return;
204+
201205
assert(!Frozen);
202-
// It's okay to mark a rule as conflicting multiple times, but it must not
203-
// be a permanent rule.
204206
assert(!Permanent && "Permanent rule should not conflict with anything");
205207
Conflicting = true;
206208
}

lib/AST/TypeCheckRequests.cpp

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -340,28 +340,26 @@ void RequirementSignatureRequest::cacheResult(RequirementSignature value) const
340340
// Requirement computation.
341341
//----------------------------------------------------------------------------//
342342

343-
WhereClauseOwner::WhereClauseOwner(GenericContext *genCtx): dc(genCtx) {
344-
if (const auto whereClause = genCtx->getTrailingWhereClause())
345-
source = whereClause;
346-
else
347-
source = genCtx->getGenericParams();
348-
}
343+
WhereClauseOwner::WhereClauseOwner(GenericContext *genCtx)
344+
: dc(genCtx),
345+
source(genCtx->getTrailingWhereClause()) {}
349346

350347
WhereClauseOwner::WhereClauseOwner(AssociatedTypeDecl *atd)
351348
: dc(atd->getInnermostDeclContext()),
352349
source(atd->getTrailingWhereClause()) {}
353350

354351
SourceLoc WhereClauseOwner::getLoc() const {
355-
if (auto where = source.dyn_cast<TrailingWhereClause *>())
356-
return where->getWhereLoc();
357-
358-
if (auto attr = source.dyn_cast<SpecializeAttr *>())
352+
if (auto genericParams = source.dyn_cast<GenericParamList *>()) {
353+
return genericParams->getWhereLoc();
354+
} else if (auto attr = source.dyn_cast<SpecializeAttr *>()) {
359355
return attr->getLocation();
360-
361-
if (auto attr = source.dyn_cast<DifferentiableAttr *>())
356+
} else if (auto attr = source.dyn_cast<DifferentiableAttr *>()) {
362357
return attr->getLocation();
358+
} else if (auto where = source.dyn_cast<TrailingWhereClause *>()) {
359+
return where->getWhereLoc();
360+
}
363361

364-
return source.get<GenericParamList *>()->getWhereLoc();
362+
return SourceLoc();
365363
}
366364

367365
void swift::simple_display(llvm::raw_ostream &out,

0 commit comments

Comments
 (0)