Skip to content

Commit e67be5c

Browse files
authored
Merge pull request #82690 from slavapestov/fix-rdar116938972
Sema: Improve the 'protocol method can't impose new requirements on Self' check
2 parents c6f223f + 4dc0db3 commit e67be5c

File tree

4 files changed

+174
-31
lines changed

4 files changed

+174
-31
lines changed

include/swift/AST/DiagnosticEngine.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,12 @@ namespace swift {
401401
/// until the next major language version.
402402
InFlightDiagnostic &warnUntilFutureSwiftVersion();
403403

404+
InFlightDiagnostic &warnUntilFutureSwiftVersionIf(bool shouldLimit) {
405+
if (!shouldLimit)
406+
return *this;
407+
return warnUntilFutureSwiftVersion();
408+
}
409+
404410
/// Limit the diagnostic behavior to warning until the specified version.
405411
///
406412
/// This helps stage in fixes for stricter diagnostics as warnings

lib/Sema/TypeCheckGeneric.cpp

Lines changed: 138 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -267,49 +267,157 @@ OpaqueResultTypeRequest::evaluate(Evaluator &evaluator,
267267
return opaqueDecl;
268268
}
269269

270-
/// Determine whether the given type is \c Self, an associated type of \c Self,
271-
/// or a concrete type.
272-
static bool isSelfDerivedOrConcrete(Type protoSelf, Type type) {
273-
// Check for a concrete type.
274-
if (!type->hasTypeParameter())
275-
return true;
270+
static bool checkProtocolSelfRequirementsImpl(
271+
ASTContext &ctx, ProtocolDecl *proto, ValueDecl *decl,
272+
GenericSignature originalSig,
273+
GenericSignature effectiveSig,
274+
bool downgrade,
275+
bool *hasSameTypeRequirement) {
276+
if (hasSameTypeRequirement)
277+
*hasSameTypeRequirement = false;
278+
279+
for (auto req : effectiveSig.getRequirements()) {
280+
if (req.getKind() == RequirementKind::SameType)
281+
if (hasSameTypeRequirement)
282+
*hasSameTypeRequirement = true;
283+
284+
// The conformance of 'Self' to the protocol is okay.
285+
if (req.getKind() == RequirementKind::Conformance &&
286+
req.getProtocolDecl() == proto &&
287+
req.getFirstType()->isEqual(ctx.TheSelfType))
288+
continue;
289+
290+
auto isSelfDerived = [&](Type t) -> bool {
291+
return t->getRootGenericParam()->isEqual(ctx.TheSelfType);
292+
};
293+
294+
// If the requirement's subject type is rooted in an inner generic
295+
// parameter, this requirement is okay.
296+
if (!isSelfDerived(req.getFirstType()))
297+
continue;
298+
299+
Type firstType = req.getFirstType();
300+
Type secondType;
301+
if (req.getKind() == RequirementKind::Layout)
302+
secondType = ctx.getAnyObjectConstraint();
303+
else
304+
secondType = req.getSecondType();
276305

277-
if (type->isTypeParameter() &&
278-
type->getRootGenericParam()->isEqual(protoSelf))
306+
// Self.A.B == <<inner generic parameter>> is OK.
307+
if (req.getKind() == RequirementKind::SameType &&
308+
secondType->isTypeParameter() &&
309+
!isSelfDerived(secondType))
310+
continue;
311+
312+
// Anything else is a new requirement on 'Self', which is not allowed.
313+
314+
// Downgrade even harder in this case, since the old logic always missed it.
315+
if (secondType->hasTypeParameter() && !secondType->isTypeParameter())
316+
downgrade = true;
317+
318+
// Re-sugar the types, since effectiveSig might be canonical.
319+
firstType = originalSig->getSugaredType(firstType);
320+
secondType = originalSig->getSugaredType(secondType);
321+
static_assert((unsigned)RequirementKind::LAST_KIND == 4,
322+
"update %select in diagnostic!");
323+
324+
ctx.Diags.diagnose(decl, diag::requirement_restricts_self, decl,
325+
firstType.getString(),
326+
static_cast<unsigned>(req.getKind()),
327+
secondType.getString())
328+
// FIXME: This should become an unconditional error since violating
329+
// this invariant can introduce compiler and run time crashes.
330+
.warnUntilFutureSwiftVersionIf(downgrade);
279331
return true;
332+
}
280333

281334
return false;
282335
}
283336

284337
// For a generic requirement in a protocol, make sure that the requirement
285338
// set didn't add any requirements to Self or its associated types.
286339
void TypeChecker::checkProtocolSelfRequirements(ValueDecl *decl) {
287-
// For a generic requirement in a protocol, make sure that the requirement
288-
// set didn't add any requirements to Self or its associated types.
340+
// Make sure the generic signature of a protocol method doesn't
341+
// impose any new requirements on Self or one of its member types.
289342
if (auto *proto = dyn_cast<ProtocolDecl>(decl->getDeclContext())) {
343+
auto *genCtx = decl->getAsGenericContext();
344+
ASSERT(genCtx != nullptr);
345+
346+
// If it's not generic and it doesn't have a where clause, there is
347+
// nothing to check.
348+
if (!genCtx->getGenericParams() && !genCtx->getTrailingWhereClause())
349+
return;
350+
290351
auto &ctx = proto->getASTContext();
291-
auto protoSelf = proto->getSelfInterfaceType();
292-
auto sig = decl->getInnermostDeclContext()->getGenericSignatureOfContext();
352+
auto sig = genCtx->getGenericSignature();
353+
354+
bool hasSameTypeRequirement = false;
355+
356+
// Perform the check as it was formerly implemented first, by looking at
357+
// the syntactic requirements of the original generic signature.
358+
if (checkProtocolSelfRequirementsImpl(ctx, proto, decl, sig, sig,
359+
/*downgrade=*/false,
360+
&hasSameTypeRequirement)) {
361+
return;
362+
}
363+
364+
// If the generic signature was sufficiently simple, we're done.
365+
if (!hasSameTypeRequirement)
366+
return;
367+
368+
// The quick check doesn't catch everything when same-type requirements
369+
// are involved, so the correct thing is to build a new signature where
370+
// the innermost generic parameters added by the protocol method now have
371+
// a non-zero weight. In this signature, any type that can be made
372+
// equivalent to a member type of Self will have a reduced type rooted
373+
// in Self.
374+
SmallVector<GenericTypeParamType *, 2> params;
375+
SmallVector<Requirement, 2> reqs;
376+
377+
auto transformParam = [&](GenericTypeParamType *paramTy)
378+
-> GenericTypeParamType * {
379+
if (paramTy->getDepth() == sig->getMaxDepth()) {
380+
return GenericTypeParamType::get(
381+
paramTy->getParamKind(),
382+
paramTy->getDepth(),
383+
paramTy->getIndex(),
384+
/*weight=*/1,
385+
paramTy->getValueType(), ctx);
386+
}
387+
388+
return cast<GenericTypeParamType>(paramTy->getCanonicalType());
389+
};
390+
391+
for (auto *paramTy : sig.getGenericParams())
392+
params.push_back(transformParam(paramTy));
393+
394+
auto transformType = [&](Type t) -> Type {
395+
return t.transformRec([&](TypeBase *t) -> std::optional<Type> {
396+
if (auto *paramTy = dyn_cast<GenericTypeParamType>(t))
397+
return transformParam(paramTy);
398+
399+
return std::nullopt;
400+
})->getCanonicalType();
401+
};
402+
293403
for (auto req : sig.getRequirements()) {
294-
// If one of the types in the requirement is dependent on a non-Self
295-
// type parameter, this requirement is okay.
296-
if (!isSelfDerivedOrConcrete(protoSelf, req.getFirstType()) ||
297-
!isSelfDerivedOrConcrete(protoSelf, req.getSecondType()))
298-
continue;
299-
300-
// The conformance of 'Self' to the protocol is okay.
301-
if (req.getKind() == RequirementKind::Conformance &&
302-
req.getProtocolDecl() == proto &&
303-
req.getFirstType()->is<GenericTypeParamType>())
304-
continue;
305-
306-
static_assert((unsigned)RequirementKind::LAST_KIND == 4,
307-
"update %select in diagnostic!");
308-
ctx.Diags.diagnose(decl, diag::requirement_restricts_self, decl,
309-
req.getFirstType().getString(),
310-
static_cast<unsigned>(req.getKind()),
311-
req.getSecondType().getString());
404+
if (req.getKind() != RequirementKind::Layout) {
405+
reqs.emplace_back(req.getKind(),
406+
transformType(req.getFirstType()),
407+
transformType(req.getSecondType()));
408+
} else {
409+
reqs.emplace_back(req.getKind(),
410+
transformType(req.getFirstType()),
411+
req.getLayoutConstraint());
412+
}
312413
}
414+
415+
auto weightedSig = buildGenericSignature(
416+
ctx, GenericSignature(), params, reqs, /*allowInverses=*/false);
417+
418+
// Repeat the check with the new signature.
419+
checkProtocolSelfRequirementsImpl(ctx, proto, decl, sig, weightedSig,
420+
/*downgrade=*/true, nullptr);
313421
}
314422
}
315423

test/decl/protocol/req/unsatisfiable.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,32 @@ class C7<T>: P7 { // expected-error {{type 'C7<T>' does not conform to protocol
9797
// expected-note@-1 {{add stubs for conformance}}
9898
typealias A = T
9999
}
100+
101+
// This used to just crash.
102+
protocol LayoutConstraint {
103+
associatedtype A
104+
105+
func f<T>(_: T) where A: AnyObject
106+
// expected-error@-1 {{instance method requirement 'f' cannot add constraint 'Self.A: AnyObject' on 'Self'}}
107+
}
108+
109+
protocol Q {
110+
associatedtype A3
111+
}
112+
113+
// We missed these cases originally.
114+
protocol ComplexDerivation {
115+
associatedtype A1
116+
associatedtype A2: Q
117+
118+
func bad1<B: Equatable>(_: B) where B == Self.A1
119+
// expected-warning@-1 {{instance method requirement 'bad1' cannot add constraint 'Self.A1: Equatable' on 'Self'; this will be an error in a future Swift language mode}}
120+
121+
func bad2<B>(_: B) where A1 == [B]
122+
// expected-warning@-1 {{instance method requirement 'bad2' cannot add constraint 'Self.A1 == [B]' on 'Self'; this will be an error in a future Swift language mode}}
123+
124+
func good<B>(_: B) where A2 == B // This is fine
125+
126+
func bad3<B, C>(_: B, _: C) where A2 == B, B.A3 == [C]
127+
// expected-warning@-1 {{instance method requirement 'bad3' cannot add constraint 'Self.A2.A3 == Array<C>' on 'Self'; this will be an error in a future Swift language mode}}
128+
}

validation-test/compiler_crashers_2/16638651735c2c8.swift renamed to validation-test/compiler_crashers_2_fixed/16638651735c2c8.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// {"kind":"typecheck","signature":"swift::TypeChecker::checkProtocolSelfRequirements(swift::ValueDecl*)"}
2-
// RUN: not --crash %target-swift-frontend -typecheck %s
2+
// RUN: not %target-swift-frontend -typecheck %s
33
protocol a {
44
associatedtype b
55
func c () where b : AnyObject

0 commit comments

Comments
 (0)