Skip to content

Commit 48f46f0

Browse files
committed
Sema: Redo the normalization in TypeResolver::resolveCompositionType()
1 parent 780c4e9 commit 48f46f0

File tree

6 files changed

+94
-131
lines changed

6 files changed

+94
-131
lines changed

lib/AST/ASTContext.cpp

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3914,15 +3914,6 @@ ProtocolCompositionType::build(const ASTContext &C, ArrayRef<Type> Members,
39143914
bool HasExplicitAnyObject) {
39153915
assert(Members.size() != 1 || HasExplicitAnyObject || !Inverses.empty());
39163916

3917-
#ifndef NDEBUG
3918-
for (auto member : Members) {
3919-
if (auto *proto = member->getAs<ProtocolType>()) {
3920-
assert(!proto->getDecl()->getInvertibleProtocolKind() &&
3921-
"Should have been folded away");
3922-
}
3923-
}
3924-
#endif
3925-
39263917
// Check to see if we've already seen this protocol composition before.
39273918
void *InsertPos = nullptr;
39283919
llvm::FoldingSetNodeID ID;

lib/AST/Type.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3777,13 +3777,6 @@ Type ProtocolCompositionType::get(const ASTContext &C,
37773777
}
37783778
}
37793779

3780-
// Drop any explicitly provided invertible protocols.
3781-
if (auto ip = proto->getInvertibleProtocolKind()) {
3782-
// We diagnose '~Copyable & Copyable' before forming the PCT.
3783-
assert(!Inverses.contains(*ip) && "opposing invertible constraints!");
3784-
continue;
3785-
}
3786-
37873780
CanTypes.push_back(proto->getDeclaredInterfaceType());
37883781
}
37893782

lib/Sema/TypeCheckType.cpp

Lines changed: 94 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -5485,146 +5485,89 @@ NeverNullType TypeResolver::resolveTupleType(TupleTypeRepr *repr,
54855485
return TupleType::get(elements, ctx);
54865486
}
54875487

5488-
/// \returns the inverse ~P that is conflicted if a protocol in \c tys
5489-
/// requires P. For example, `Q & ~Copyable` is a conflict, if `Q` requires
5490-
/// or is equal to `Copyable`
5491-
static std::optional<InvertibleProtocolKind>
5492-
hasConflictedInverse(ArrayRef<Type> tys, InvertibleProtocolSet inverses) {
5493-
// Fast-path: no inverses that could be conflicted!
5494-
if (inverses.empty())
5495-
return std::nullopt;
5496-
5497-
for (auto ty : tys) {
5498-
// Handle nested PCT's recursively since we haven't flattened them away yet.
5499-
if (auto pct = dyn_cast<ProtocolCompositionType>(ty)) {
5500-
if (auto conflict = hasConflictedInverse(pct->getMembers(), inverses))
5501-
return conflict;
5502-
continue;
5503-
}
5504-
5505-
// Dig out a protocol.
5506-
ProtocolDecl *decl = nullptr;
5507-
if (auto protoTy = dyn_cast<ProtocolType>(ty))
5508-
decl = protoTy->getDecl();
5509-
else if (auto paramProtoTy = dyn_cast<ParameterizedProtocolType>(ty))
5510-
decl = paramProtoTy->getProtocol();
5511-
5512-
if (!decl)
5513-
continue;
5514-
5515-
// If an inverse ~I exists for this protocol member of the PCT that
5516-
// requires I, then it's a conflict.
5517-
for (auto inverse : inverses) {
5518-
if (decl->isSpecificProtocol(getKnownProtocolKind(inverse))
5519-
|| decl->requiresInvertible(inverse)) {
5520-
return inverse;
5521-
}
5522-
}
5523-
}
5524-
5525-
return std::nullopt;
5526-
}
5527-
55285488
NeverNullType
55295489
TypeResolver::resolveCompositionType(CompositionTypeRepr *repr,
55305490
TypeResolutionOptions options) {
55315491

5492+
SmallVector<Type, 4> Members;
5493+
55325494
// Note that the superclass type will appear as part of one of the
55335495
// types in 'Members', so it's not used when constructing the
55345496
// fully-realized type below -- but we just record it to make sure
55355497
// there is only one superclass.
55365498
Type SuperclassType;
5537-
SmallVector<Type, 4> Members;
5538-
InvertibleProtocolSet Inverses;
5539-
bool HasAnyObject = false;
55405499

5541-
// Whether we saw at least one protocol. A protocol composition
5542-
// must either be empty (in which case it is Any or AnyObject),
5543-
// or if it has a superclass constraint, have at least one protocol.
5544-
bool HasProtocol = false;
5500+
// Did we see at least one protocol or inverse?
5501+
bool HasNonClassMember = false;
55455502

5546-
auto checkSuperclass = [&](SourceLoc loc, Type t) -> bool {
5547-
if (SuperclassType && !SuperclassType->isEqual(t)) {
5548-
diagnose(loc, diag::protocol_composition_one_class, t,
5503+
// If true, we cannot form a composition from these members.
5504+
bool IsInvalid = false;
5505+
5506+
std::function<void (SourceLoc, Type)> checkMember
5507+
= [&](SourceLoc loc, Type ty) {
5508+
if (auto pct = ty->getAs<ProtocolCompositionType>()) {
5509+
if (!pct->getInverses().empty())
5510+
HasNonClassMember = true;
5511+
5512+
for (auto member : pct->getMembers())
5513+
checkMember(loc, member);
5514+
return;
5515+
}
5516+
5517+
if (ty->is<ProtocolType>() ||
5518+
ty->is<ParameterizedProtocolType>()) {
5519+
HasNonClassMember = true;
5520+
return;
5521+
}
5522+
5523+
assert(isa<ClassDecl>(ty->getAnyNominal()));
5524+
5525+
if (SuperclassType && !SuperclassType->isEqual(ty)) {
5526+
diagnose(loc, diag::protocol_composition_one_class, ty,
55495527
SuperclassType);
5550-
return true;
5528+
IsInvalid = true;
5529+
return;
55515530
}
55525531

5553-
SuperclassType = t;
5554-
return false;
5532+
SuperclassType = ty;
55555533
};
55565534

5557-
bool IsInvalid = false;
5558-
55595535
for (auto tyR : repr->getTypes()) {
55605536
auto ty = resolveType(tyR,
55615537
options.withContext(TypeResolverContext::GenericRequirement));
55625538
if (ty->hasError()) return ty;
55635539

5564-
auto nominalDecl = ty->getAnyNominal();
5565-
if (isa_and_nonnull<ClassDecl>(nominalDecl)) {
5566-
if (checkSuperclass(tyR->getStartLoc(), ty))
5567-
continue;
5568-
5540+
if (ty->is<ProtocolType>()) {
5541+
checkMember(tyR->getStartLoc(), ty);
55695542
Members.push_back(ty);
55705543
continue;
55715544
}
55725545

55735546
// FIXME: Support compositions involving parameterized protocol types,
55745547
// like 'any Collection<String> & Sendable', etc.
5575-
if (ty->isConstraintType()) {
5576-
if (ty->is<ProtocolType>()) {
5577-
HasProtocol = true;
5578-
Members.push_back(ty);
5579-
continue;
5580-
}
5581-
5582-
if (ty->is<ParameterizedProtocolType>() &&
5583-
!options.isConstraintImplicitExistential() &&
5584-
options.getContext() != TypeResolverContext::ExistentialConstraint) {
5585-
HasProtocol = true;
5586-
Members.push_back(ty);
5587-
continue;
5588-
}
5548+
if (ty->is<ParameterizedProtocolType>() &&
5549+
!options.isConstraintImplicitExistential() &&
5550+
options.getContext() != TypeResolverContext::ExistentialConstraint) {
5551+
checkMember(tyR->getStartLoc(), ty);
5552+
Members.push_back(ty);
5553+
continue;
5554+
}
55895555

5590-
if (auto pct = ty->getAs<ProtocolCompositionType>()) {
5591-
auto layout = ty->getExistentialLayout();
5592-
if (auto superclass = layout.explicitSuperclass)
5593-
if (checkSuperclass(tyR->getStartLoc(), superclass))
5594-
continue;
5595-
if (!layout.getProtocols().empty())
5596-
HasProtocol = true;
5597-
if (layout.hasExplicitAnyObject)
5598-
HasAnyObject = true;
5556+
if (ty->is<ProtocolCompositionType>()) {
5557+
checkMember(tyR->getStartLoc(), ty);
5558+
Members.push_back(ty);
5559+
continue;
5560+
}
55995561

5600-
Inverses.insertAll(pct->getInverses());
5601-
Members.push_back(ty);
5602-
continue;
5603-
}
5562+
if (isa_and_nonnull<ClassDecl>(ty->getAnyNominal())) {
5563+
checkMember(tyR->getStartLoc(), ty);
5564+
Members.push_back(ty);
5565+
continue;
56045566
}
56055567

56065568
diagnose(tyR->getStartLoc(),
56075569
diag::invalid_protocol_composition_member,
56085570
ty);
5609-
5610-
IsInvalid = true;
5611-
}
5612-
5613-
// Cannot combine inverses with Superclass or AnyObject in a composition.
5614-
if ((SuperclassType || HasAnyObject) && !Inverses.empty()) {
5615-
diagnose(repr->getStartLoc(),
5616-
diag::inverse_with_class_constraint,
5617-
HasAnyObject,
5618-
getProtocolName(getKnownProtocolKind(*Inverses.begin())),
5619-
SuperclassType);
5620-
IsInvalid = true;
5621-
}
5622-
5623-
// Cannot provide an inverse in the same composition requiring the protocol.
5624-
if (auto conflict = hasConflictedInverse(Members, Inverses)) {
5625-
diagnose(repr->getLoc(),
5626-
diag::inverse_conflicts_explicit_composition,
5627-
getProtocolName(getKnownProtocolKind(*conflict)));
56285571
IsInvalid = true;
56295572
}
56305573

@@ -5636,17 +5579,58 @@ TypeResolver::resolveCompositionType(CompositionTypeRepr *repr,
56365579
// Avoid confusing diagnostics ('MyClass' not convertible to 'MyClass',
56375580
// etc) by collapsing a composition consisting of a single class down
56385581
// to the class itself.
5639-
if (SuperclassType && !HasProtocol)
5582+
if (SuperclassType && !HasNonClassMember)
56405583
return SuperclassType;
56415584

5642-
// In user-written types, AnyObject constraints always refer to the
5643-
// AnyObject type in the standard library.
56445585
auto composition =
5645-
ProtocolCompositionType::get(getASTContext(), Members, Inverses,
5586+
ProtocolCompositionType::get(getASTContext(), Members,
5587+
/*Inverses=*/{},
56465588
/*HasExplicitAnyObject=*/false);
5589+
5590+
// Flatten the composition.
5591+
if (auto canComposition = dyn_cast<ProtocolCompositionType>(
5592+
composition->getCanonicalType())) {
5593+
auto inverses = canComposition->getInverses();
5594+
auto layout = composition->getExistentialLayout();
5595+
5596+
// Cannot provide an inverse in the same composition requiring the protocol.
5597+
for (auto ip : inverses) {
5598+
auto kp = getKnownProtocolKind(ip);
5599+
5600+
if (layout.requiresClass()) {
5601+
bool hasExplicitAnyObject = layout.hasExplicitAnyObject;
5602+
diagnose(repr->getStartLoc(),
5603+
diag::inverse_with_class_constraint,
5604+
hasExplicitAnyObject,
5605+
getProtocolName(kp),
5606+
layout.getSuperclass());
5607+
IsInvalid = true;
5608+
break;
5609+
}
5610+
5611+
auto *proto = getASTContext().getProtocol(kp);
5612+
for (auto *otherProto : layout.getProtocols()) {
5613+
if (proto == otherProto ||
5614+
otherProto->inheritsFrom(proto)) {
5615+
diagnose(repr->getLoc(),
5616+
diag::inverse_conflicts_explicit_composition,
5617+
getProtocolName(kp));
5618+
IsInvalid = true;
5619+
break;
5620+
}
5621+
}
5622+
}
5623+
}
5624+
5625+
if (IsInvalid) {
5626+
repr->setInvalid();
5627+
return ErrorType::get(getASTContext());
5628+
}
5629+
56475630
if (options.isConstraintImplicitExistential()) {
56485631
return ExistentialType::get(composition);
56495632
}
5633+
56505634
return composition;
56515635
}
56525636

test/Generics/inverse_generics.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,6 @@ func checkExistentialAndClasses(
409409
_ a: any AnyObject & ~Copyable, // expected-error {{composition involving 'AnyObject' cannot contain '~Copyable'}}
410410
_ b: any Soup & Copyable & ~Escapable & ~Copyable,
411411
// expected-error@-1 {{composition involving class requirement 'Soup' cannot contain '~Copyable'}}
412-
// expected-error@-2 {{composition cannot contain '~Copyable' when another member requires 'Copyable'}}
413412
_ c: some (~Escapable & Removed) & Soup // expected-error {{composition cannot contain '~Escapable' when another member requires 'Escapable'}}
414413
) {}
415414

test/Interpreter/subclass_existentials.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
// RUN: %target-codesign %t/a.out
1616
// RUN: %target-run %t/a.out
1717

18-
// XFAIL: noncopyable_generics
19-
2018
// REQUIRES: executable_test
2119

2220
import StdlibUnittest

test/type/subclass_composition.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
// RUN: %target-typecheck-verify-swift
22

3-
// XFAIL: noncopyable_generics
4-
53
protocol P1 {
64
typealias DependentInConcreteConformance = Self
75
}

0 commit comments

Comments
 (0)