Skip to content

Commit 33f34d1

Browse files
authored
Merge pull request #35281 from xedin/rdar-72819046
[Diagnostics] Special case requirement failures related to `return` statement/expression
2 parents 5e5f484 + f0ff68e commit 33f34d1

File tree

8 files changed

+120
-43
lines changed

8 files changed

+120
-43
lines changed

include/swift/Sema/ConstraintLocator.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,20 @@ class LocatorPathElt::ArgumentAttribute final : public StoredIntegerElement<1> {
762762
}
763763
};
764764

765+
class LocatorPathElt::ConformanceRequirement final
766+
: public StoredPointerElement<ProtocolDecl> {
767+
public:
768+
ConformanceRequirement(ProtocolDecl *protocol)
769+
: StoredPointerElement(PathElementKind::ConformanceRequirement,
770+
protocol) {}
771+
772+
ProtocolDecl *getRequirement() const { return getStoredPointer(); }
773+
774+
static bool classof(const LocatorPathElt *elt) {
775+
return elt->getKind() == ConstraintLocator::ConformanceRequirement;
776+
}
777+
};
778+
765779
namespace details {
766780
template <typename CustomPathElement>
767781
class PathElement {

include/swift/Sema/ConstraintLocatorPathElts.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,9 @@ CUSTOM_LOCATOR_PATH_ELT(ArgumentAttribute)
195195
/// The result of a chain of member accesses off an UnresolvedMemberExpr
196196
SIMPLE_LOCATOR_PATH_ELT(UnresolvedMemberChainResult)
197197

198+
/// The conformance requirement associated with a type.
199+
CUSTOM_LOCATOR_PATH_ELT(ConformanceRequirement)
200+
198201
#undef LOCATOR_PATH_ELT
199202
#undef CUSTOM_LOCATOR_PATH_ELT
200203
#undef SIMPLE_LOCATOR_PATH_ELT

lib/Sema/CSApply.cpp

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7972,10 +7972,12 @@ static Optional<SolutionApplicationTarget> applySolutionToForEachStmt(
79727972
auto stmt = forEachStmtInfo.stmt;
79737973
auto sequenceProto = TypeChecker::getProtocol(
79747974
cs.getASTContext(), stmt->getForLoc(), KnownProtocolKind::Sequence);
7975-
auto contextualLocator = cs.getConstraintLocator(
7976-
target.getAsExpr(), LocatorPathElt::ContextualType());
7977-
auto sequenceConformance = solution.resolveConformance(
7978-
contextualLocator, sequenceProto);
7975+
auto contextualLocator = solution.getConstraintLocator(
7976+
target.getAsExpr(),
7977+
{LocatorPathElt::ContextualType(),
7978+
LocatorPathElt::ConformanceRequirement(sequenceProto)});
7979+
auto sequenceConformance =
7980+
solution.resolveConformance(contextualLocator, sequenceProto);
79797981
assert(!sequenceConformance.isInvalid() &&
79807982
"Couldn't find sequence conformance");
79817983

@@ -8386,32 +8388,30 @@ Expr *Solution::coerceToType(Expr *expr, Type toType,
83868388

83878389
ProtocolConformanceRef Solution::resolveConformance(
83888390
ConstraintLocator *locator, ProtocolDecl *proto) {
8389-
for (const auto &conformance : Conformances) {
8390-
if (conformance.first != locator)
8391-
continue;
8392-
if (conformance.second.getRequirement() != proto)
8393-
continue;
8394-
8395-
// If the conformance doesn't require substitution, return it immediately.
8396-
auto conformanceRef = conformance.second;
8397-
if (conformanceRef.isAbstract())
8398-
return conformanceRef;
8399-
8400-
auto concrete = conformanceRef.getConcrete();
8401-
auto conformingType = concrete->getType();
8402-
if (!conformingType->hasTypeVariable())
8403-
return conformanceRef;
8404-
8405-
// Substitute into the conformance type, then look for a conformance
8406-
// again.
8407-
// FIXME: Should be able to perform the substitution using the Solution
8408-
// itself rather than another conforms-to-protocol check.
8409-
Type substConformingType = simplifyType(conformingType);
8410-
return TypeChecker::conformsToProtocol(
8411-
substConformingType, proto, constraintSystem->DC);
8412-
}
8413-
8414-
return ProtocolConformanceRef::forInvalid();
8391+
auto conformance = llvm::find_if(Conformances, [&locator](const auto &elt) {
8392+
return elt.first == locator;
8393+
});
8394+
8395+
if (conformance == Conformances.end())
8396+
return ProtocolConformanceRef::forInvalid();
8397+
8398+
// If the conformance doesn't require substitution, return it immediately.
8399+
auto conformanceRef = conformance->second;
8400+
if (conformanceRef.isAbstract())
8401+
return conformanceRef;
8402+
8403+
auto concrete = conformanceRef.getConcrete();
8404+
auto conformingType = concrete->getType();
8405+
if (!conformingType->hasTypeVariable())
8406+
return conformanceRef;
8407+
8408+
// Substitute into the conformance type, then look for a conformance
8409+
// again.
8410+
// FIXME: Should be able to perform the substitution using the Solution
8411+
// itself rather than another conforms-to-protocol check.
8412+
Type substConformingType = simplifyType(conformingType);
8413+
return TypeChecker::conformsToProtocol(substConformingType, proto,
8414+
constraintSystem->DC);
84158415
}
84168416

84178417
bool Solution::hasType(ASTNode node) const {

lib/Sema/CSDiagnostics.cpp

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -211,12 +211,12 @@ ProtocolConformance *RequirementFailure::getConformanceForConditionalReq(
211211
return nullptr;
212212

213213
auto path = locator->getPath();
214-
auto *typeReqLoc = getConstraintLocator(getRawAnchor(), path.drop_back());
214+
auto *conformanceLoc = getConstraintLocator(getRawAnchor(), path.drop_back());
215215

216216
auto result = llvm::find_if(
217217
solution.Conformances,
218218
[&](const std::pair<ConstraintLocator *, ProtocolConformanceRef>
219-
&conformance) { return conformance.first == typeReqLoc; });
219+
&conformance) { return conformance.first == conformanceLoc; });
220220
assert(result != solution.Conformances.end());
221221

222222
auto conformance = result->second;
@@ -249,8 +249,28 @@ ValueDecl *RequirementFailure::getDeclRef() const {
249249
return getAffectedDeclFromType(func->getResultInterfaceType());
250250
}
251251

252-
if (isFromContextualType())
253-
return getAffectedDeclFromType(getContextualType(getRawAnchor()));
252+
if (isFromContextualType()) {
253+
auto anchor = getRawAnchor();
254+
auto contextualPurpose = getContextualTypePurpose(anchor);
255+
auto contextualTy = getContextualType(anchor);
256+
257+
// If the issue is a mismatch between `return` statement/expression
258+
// and its contextual requirements, it means that affected declaration
259+
// is a declarer of a contextual "result" type e.g. member of a
260+
// type, local function etc.
261+
if (contextualPurpose == CTP_ReturnStmt ||
262+
contextualPurpose == CTP_ReturnSingleExpr) {
263+
// In case of opaque result type, let's point to the declaration
264+
// associated with the type itself (since it has one) instead of
265+
// declarer.
266+
if (auto *opaque = contextualTy->getAs<OpaqueTypeArchetypeType>())
267+
return opaque->getDecl();
268+
269+
return cast<ValueDecl>(getDC()->getAsDecl());
270+
}
271+
272+
return getAffectedDeclFromType(contextualTy);
273+
}
254274

255275
if (auto overload = getCalleeOverloadChoiceIfAvailable(getLocator())) {
256276
// If there is a declaration associated with this

lib/Sema/CSSimplify.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5716,21 +5716,23 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
57165716
/// Record the given conformance as the result, adding any conditional
57175717
/// requirements if necessary.
57185718
auto recordConformance = [&](ProtocolConformanceRef conformance) {
5719+
auto *conformanceLoc = getConstraintLocator(
5720+
loc, LocatorPathElt::ConformanceRequirement(protocol));
57195721
// Record the conformance.
5720-
CheckedConformances.push_back({loc, conformance});
5722+
CheckedConformances.push_back({conformanceLoc, conformance});
57215723

5722-
if (isConformanceUnavailable(conformance, loc))
5724+
if (isConformanceUnavailable(conformance, conformanceLoc))
57235725
increaseScore(SK_Unavailable);
57245726

57255727
// This conformance may be conditional, in which case we need to consider
57265728
// those requirements as constraints too.
57275729
if (conformance.isConcrete()) {
57285730
unsigned index = 0;
57295731
for (const auto &req : conformance.getConditionalRequirements()) {
5730-
addConstraint(req,
5731-
locator.withPathElement(
5732-
LocatorPathElt::ConditionalRequirement(
5733-
index++, req.getKind())));
5732+
addConstraint(
5733+
req, getConstraintLocator(conformanceLoc,
5734+
LocatorPathElt::ConditionalRequirement(
5735+
index++, req.getKind())));
57345736
}
57355737
}
57365738

lib/Sema/ConstraintLocator.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ unsigned LocatorPathElt::getNewSummaryFlags() const {
6666
case ConstraintLocator::KeyPathComponent:
6767
case ConstraintLocator::ConditionalRequirement:
6868
case ConstraintLocator::TypeParameterRequirement:
69+
case ConstraintLocator::ConformanceRequirement:
6970
case ConstraintLocator::ImplicitlyUnwrappedDisjunctionChoice:
7071
case ConstraintLocator::DynamicLookupResult:
7172
case ConstraintLocator::ContextualType:
@@ -397,6 +398,14 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const {
397398
break;
398399
}
399400

401+
case ConformanceRequirement: {
402+
auto reqElt = elt.castTo<LocatorPathElt::ConformanceRequirement>();
403+
out << "conformance requirement (";
404+
reqElt.getRequirement()->dumpRef(out);
405+
out << ")";
406+
break;
407+
}
408+
400409
case ImplicitlyUnwrappedDisjunctionChoice:
401410
out << "implicitly unwrapped disjunction choice";
402411
break;

lib/Sema/ConstraintSystem.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5181,15 +5181,15 @@ static Optional<Requirement> getRequirement(ConstraintSystem &cs,
51815181

51825182
if (reqLoc->isConditionalRequirement()) {
51835183
auto path = reqLocator->getPath();
5184-
auto *typeReqLoc =
5184+
auto *conformanceLoc =
51855185
cs.getConstraintLocator(reqLocator->getAnchor(), path.drop_back());
51865186

51875187
auto conformances = cs.getCheckedConformances();
51885188
auto result = llvm::find_if(
51895189
conformances,
5190-
[&typeReqLoc](
5190+
[&conformanceLoc](
51915191
const std::pair<ConstraintLocator *, ProtocolConformanceRef>
5192-
&conformance) { return conformance.first == typeReqLoc; });
5192+
&conformance) { return conformance.first == conformanceLoc; });
51935193
assert(result != conformances.end());
51945194

51955195
auto conformance = result->second;

test/Constraints/sr13992.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
// SR-13992
4+
5+
protocol V {}
6+
7+
protocol P1 {}
8+
protocol P2 {
9+
func bar() -> V
10+
}
11+
protocol P3 {}
12+
13+
struct S<T> {
14+
var foo: T
15+
}
16+
17+
extension S : P1 {}
18+
extension S : P2 where T: P3 { // expected-note {{requirement from conditional conformance of 'S<V>' to 'P2'}}
19+
func bar() -> V { fatalError() }
20+
}
21+
22+
struct Q {
23+
var foo: V
24+
25+
func test() -> P1 & P2 {
26+
S(foo: foo) // expected-error {{protocol 'V' as a type cannot conform to 'P3'}}
27+
// expected-note@-1 {{only concrete types such as structs, enums and classes can conform to protocols}}
28+
}
29+
}

0 commit comments

Comments
 (0)