Skip to content

Commit 5c24df2

Browse files
Merge pull request swiftlang#78363 from AnthonyLatsis/dicksonia-arborescens
[NFC] Sema: Tidy up `canOpenExistentialCallArgument`
2 parents 97b1548 + 56d943a commit 5c24df2

File tree

6 files changed

+115
-91
lines changed

6 files changed

+115
-91
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3277,10 +3277,11 @@ class ConstraintSystem {
32773277
ConstraintLocator *getOpenOpaqueLocator(
32783278
ConstraintLocatorBuilder locator, OpaqueTypeDecl *opaqueDecl);
32793279

3280-
/// Open the given existential type, recording the opened type in the
3281-
/// constraint system and returning both it and the root opened archetype.
3282-
std::pair<Type, OpenedArchetypeType *> openExistentialType(
3283-
Type type, ConstraintLocator *locator);
3280+
/// Open the given existential type or existential metatype, recording the
3281+
/// opened archetype in the constraint system and returning both the opened
3282+
/// type and opened archetype.
3283+
std::pair<Type, OpenedArchetypeType *>
3284+
openAnyExistentialType(Type type, ConstraintLocator *locator);
32843285

32853286
/// Update OpenedExistentials and record a change in the trail.
32863287
void recordOpenedExistentialType(ConstraintLocator *locator,

lib/Sema/CSSimplify.cpp

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1441,8 +1441,7 @@ class AllowLabelMismatches : public MatchCallArgumentListener {
14411441
}
14421442
};
14431443

1444-
static std::optional<
1445-
std::tuple<TypeVariableType *, Type, OpenedExistentialAdjustments>>
1444+
static std::optional<std::pair<TypeVariableType *, Type>>
14461445
shouldOpenExistentialCallArgument(ValueDecl *callee, unsigned paramIdx,
14471446
Type paramTy, Type argTy, Expr *argExpr,
14481447
ConstraintSystem &cs) {
@@ -1816,25 +1815,29 @@ static ConstraintSystem::TypeMatchResult matchCallArguments(
18161815

18171816
// If the argument is an existential type and the parameter is generic,
18181817
// consider opening the existential type.
1819-
if (auto existentialArg = shouldOpenExistentialCallArgument(
1818+
if (auto typeVarAndBindingTy = shouldOpenExistentialCallArgument(
18201819
callee, paramIdx, paramTy, argTy, argExpr, cs)) {
18211820
// My kingdom for a decent "if let" in C++.
1822-
TypeVariableType *openedTypeVar;
1823-
Type existentialType;
1824-
OpenedExistentialAdjustments adjustments;
1825-
std::tie(openedTypeVar, existentialType, adjustments) = *existentialArg;
1821+
TypeVariableType *typeVar;
1822+
Type bindingTy;
1823+
std::tie(typeVar, bindingTy) = *typeVarAndBindingTy;
18261824

1827-
OpenedArchetypeType *opened;
1828-
std::tie(argTy, opened) = cs.openExistentialType(
1829-
existentialType, cs.getConstraintLocator(loc));
1825+
OpenedArchetypeType *openedArchetype;
18301826

1831-
if (adjustments.contains(OpenedExistentialAdjustmentFlags::LValue))
1832-
argTy = LValueType::get(argTy);
1827+
// Open the argument type.
1828+
argTy = argTy.transformRec([&](TypeBase *t) -> std::optional<Type> {
1829+
if (t->isAnyExistentialType()) {
1830+
Type openedTy;
1831+
std::tie(openedTy, openedArchetype) =
1832+
cs.openAnyExistentialType(t, cs.getConstraintLocator(loc));
18331833

1834-
if (adjustments.contains(OpenedExistentialAdjustmentFlags::InOut))
1835-
argTy = InOutType::get(argTy);
1834+
return openedTy;
1835+
}
1836+
1837+
return std::nullopt;
1838+
});
18361839

1837-
openedExistentials.push_back({openedTypeVar, opened});
1840+
openedExistentials.push_back({typeVar, openedArchetype});
18381841
}
18391842

18401843
// If we have a compound function reference (e.g `fn($x:)`), respect
@@ -8638,7 +8641,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
86388641
if (type->isExistentialType()) {
86398642
if (auto elt = loc->getLastElementAs<LocatorPathElt::ContextualType>()) {
86408643
if (elt->getPurpose() == CTP_ForEachSequence) {
8641-
type = openExistentialType(type, loc).first;
8644+
type = openAnyExistentialType(type, loc).first;
86428645
}
86438646
}
86448647
}
@@ -12421,8 +12424,8 @@ ConstraintSystem::simplifyOpenedExistentialOfConstraint(
1242112424
if (type2->isAnyExistentialType()) {
1242212425
// We have the existential side. Produce an opened archetype and bind
1242312426
// type1 to it.
12424-
Type openedTy = openExistentialType(type2, getConstraintLocator(locator))
12425-
.first;
12427+
Type openedTy =
12428+
openAnyExistentialType(type2, getConstraintLocator(locator)).first;
1242612429
return matchTypes(type1, openedTy, ConstraintKind::Bind, subflags, locator);
1242712430
}
1242812431
if (!type2->isTypeVariableOrMember())

lib/Sema/ConstraintSystem.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -853,8 +853,9 @@ ConstraintLocator *ConstraintSystem::getOpenOpaqueLocator(
853853
{ LocatorPathElt::OpenedOpaqueArchetype(opaqueDecl) }, 0);
854854
}
855855

856-
std::pair<Type, OpenedArchetypeType *> ConstraintSystem::openExistentialType(
857-
Type type, ConstraintLocator *locator) {
856+
std::pair<Type, OpenedArchetypeType *>
857+
ConstraintSystem::openAnyExistentialType(Type type,
858+
ConstraintLocator *locator) {
858859
Type result = OpenedArchetypeType::getAny(type);
859860
Type t = result;
860861
while (t->is<MetatypeType>())

lib/Sema/OpenedExistentials.cpp

Lines changed: 39 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -590,8 +590,7 @@ swift::isMemberAvailableOnExistential(Type baseTy, const ValueDecl *member) {
590590
return result;
591591
}
592592

593-
std::optional<
594-
std::tuple<TypeVariableType *, Type, OpenedExistentialAdjustments>>
593+
std::optional<std::pair<TypeVariableType *, Type>>
595594
swift::canOpenExistentialCallArgument(ValueDecl *callee, unsigned paramIdx,
596595
Type paramTy, Type argTy) {
597596
if (!callee)
@@ -623,24 +622,6 @@ swift::canOpenExistentialCallArgument(ValueDecl *callee, unsigned paramIdx,
623622
if (!paramTy->hasTypeVariable())
624623
return std::nullopt;
625624

626-
OpenedExistentialAdjustments adjustments;
627-
628-
// The argument may be a "var" instead of a "let".
629-
if (auto lv = argTy->getAs<LValueType>()) {
630-
argTy = lv->getObjectType();
631-
adjustments |= OpenedExistentialAdjustmentFlags::LValue;
632-
}
633-
634-
// If the argument is inout, strip it off and we can add it back.
635-
if (auto inOutArg = argTy->getAs<InOutType>()) {
636-
argTy = inOutArg->getObjectType();
637-
adjustments |= OpenedExistentialAdjustmentFlags::InOut;
638-
}
639-
640-
// The argument type needs to be an existential type or metatype thereof.
641-
if (!argTy->isAnyExistentialType())
642-
return std::nullopt;
643-
644625
auto param = getParameterAt(callee, paramIdx);
645626
if (!param)
646627
return std::nullopt;
@@ -649,26 +630,40 @@ swift::canOpenExistentialCallArgument(ValueDecl *callee, unsigned paramIdx,
649630
if (param->isVariadic())
650631
return std::nullopt;
651632

652-
// Look through an inout and an optional type on the parameter types.
653-
auto formalParamTy = param->getInterfaceType()->getInOutObjectType()
654-
->lookThroughSingleOptionalType();
655-
// Look through an inout and optional types on the parameter.
656-
paramTy = paramTy->getInOutObjectType()->lookThroughSingleOptionalType();
657-
658-
// If the argument is of an existential metatype, look through the
659-
// metatype on the parameter.
660-
if (argTy->is<AnyMetatypeType>()) {
661-
formalParamTy = formalParamTy->getMetatypeInstanceType();
662-
paramTy = paramTy->getMetatypeInstanceType();
663-
}
664-
665-
// The parameter type must be a type variable.
666-
auto paramTypeVar = paramTy->getAs<TypeVariableType>();
667-
if (!paramTypeVar)
633+
// The rvalue argument type needs to be an existential type or metatype
634+
// thereof.
635+
const auto rValueArgTy = argTy->getWithoutSpecifierType();
636+
if (!rValueArgTy->isAnyExistentialType())
668637
return std::nullopt;
669638

670-
auto genericParam = formalParamTy->getAs<GenericTypeParamType>();
671-
if (!genericParam)
639+
GenericTypeParamType *genericParam;
640+
TypeVariableType *typeVar;
641+
Type bindingTy;
642+
643+
std::tie(genericParam, typeVar, bindingTy) = [=] {
644+
// Look through an inout and optional type.
645+
Type genericParam = param->getInterfaceType()
646+
->getInOutObjectType()
647+
->lookThroughSingleOptionalType();
648+
Type typeVar =
649+
paramTy->getInOutObjectType()->lookThroughSingleOptionalType();
650+
651+
Type bindingTy = rValueArgTy;
652+
653+
// Look through a metatype.
654+
if (genericParam->is<AnyMetatypeType>()) {
655+
genericParam = genericParam->getMetatypeInstanceType();
656+
typeVar = typeVar->getMetatypeInstanceType();
657+
bindingTy = bindingTy->getMetatypeInstanceType();
658+
}
659+
660+
return std::tuple(genericParam->getAs<GenericTypeParamType>(),
661+
typeVar->getAs<TypeVariableType>(), bindingTy);
662+
}();
663+
664+
// The should have reached a type variable and corresponding generic
665+
// parameter.
666+
if (!typeVar || !genericParam)
672667
return std::nullopt;
673668

674669
// Only allow opening the innermost generic parameters.
@@ -681,14 +676,11 @@ swift::canOpenExistentialCallArgument(ValueDecl *callee, unsigned paramIdx,
681676
if (genericParam->getDepth() < genericSig->getMaxDepth())
682677
return std::nullopt;
683678

684-
Type existentialTy;
685-
if (auto existentialMetaTy = argTy->getAs<ExistentialMetatypeType>())
686-
existentialTy = existentialMetaTy->getInstanceType();
687-
else
688-
existentialTy = argTy;
689-
690-
ASSERT(existentialTy->isAnyExistentialType());
691-
679+
// The binding could be an existential metatype. Get the instance type for
680+
// conformance checks and to build an opened existential signature. If the
681+
// instance type is not an existential type, i.e., the metatype is nested,
682+
// bail out.
683+
const Type existentialTy = bindingTy->getMetatypeInstanceType();
692684
if (!existentialTy->isExistentialType())
693685
return std::nullopt;
694686

@@ -726,7 +718,7 @@ swift::canOpenExistentialCallArgument(ValueDecl *callee, unsigned paramIdx,
726718
if (referenceInfo.hasNonCovariantRef())
727719
return std::nullopt;
728720

729-
return std::make_tuple(paramTypeVar, argTy, adjustments);
721+
return std::pair(typeVar, bindingTy);
730722
}
731723

732724
/// For each occurrence of a type **type** in `refTy` that satisfies

lib/Sema/OpenedExistentials.h

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -132,19 +132,10 @@ enum class ExistentialMemberAccessLimitation : uint8_t {
132132
ExistentialMemberAccessLimitation
133133
isMemberAvailableOnExistential(Type baseTy, const ValueDecl *member);
134134

135-
/// Flags that should be applied to the existential argument type after
136-
/// opening.
137-
enum class OpenedExistentialAdjustmentFlags {
138-
/// The argument should be made inout after opening.
139-
InOut = 0x01,
140-
LValue = 0x02,
141-
};
142-
143-
using OpenedExistentialAdjustments =
144-
OptionSet<OpenedExistentialAdjustmentFlags>;
145-
146-
/// Determine whether we should open up the existential argument to the
147-
/// given parameters.
135+
/// Determine whether opening an existential argument for a function parameter
136+
/// is supported.
137+
/// A necessary condition for this is that the parameter interface type contains
138+
/// a generic parameter type to which the opened argument can bind.
148139
///
149140
/// \param callee The function or subscript being called.
150141
/// \param paramIdx The index specifying which function parameter is being
@@ -153,13 +144,9 @@ using OpenedExistentialAdjustments =
153144
/// system.
154145
/// \param argTy The type of the argument.
155146
///
156-
/// \returns If the argument type is existential and opening it can bind a
157-
/// generic parameter in the callee, returns the type variable (from the opened
158-
/// parameter type) the existential type that needs to be opened (from the
159-
/// argument type), and the adjustments that need to be applied to the
160-
/// existential type after it is opened.
161-
std::optional<
162-
std::tuple<TypeVariableType *, Type, OpenedExistentialAdjustments>>
147+
/// \returns If opening is supported, returns the type variable representing the
148+
/// generic parameter type, and the unopened type it binds to.
149+
std::optional<std::pair<TypeVariableType *, Type>>
163150
canOpenExistentialCallArgument(ValueDecl *callee, unsigned paramIdx,
164151
Type paramTy, Type argTy);
165152

test/Constraints/opened_existentials.swift

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,3 +498,43 @@ do {
498498
// CHECK: open_existential_expr {{.*}} location={{.*}}:[[@LINE+1]]:{{[0-9]+}} range=
499499
foo(&lValueP)
500500
}
501+
502+
do {
503+
do {
504+
func foo<T : BitwiseCopyable>(_: T) -> T {}
505+
506+
let exist: any Any.Type
507+
// CHECK: open_existential_expr {{.*}} location={{.*}}:[[@LINE+1]]:{{[0-9]+}} range=
508+
let result = foo(exist)
509+
do {
510+
// FIXME: The result type should be 'any Any.Type'
511+
// var types = SwiftTypePair(typeOf: result, type2: SwiftType<any Any.Type>.self)
512+
var types = SwiftTypePair(typeOf: result, type2: SwiftType<Any>.self)
513+
types.assertTypesAreEqual()
514+
}
515+
}
516+
517+
do {
518+
func foo<T : BitwiseCopyable>(_: T) -> T {}
519+
520+
let exist: any Any.Type.Type
521+
// CHECK-NOT: open_existential_expr {{.*}} location={{.*}}:[[@LINE+1]]:{{[0-9]+}} range=
522+
let result = foo(exist)
523+
do {
524+
var types = SwiftTypePair(typeOf: result, type2: SwiftType<any Any.Type.Type>.self)
525+
types.assertTypesAreEqual()
526+
}
527+
}
528+
529+
do {
530+
func foo<T>(_: T) -> T {}
531+
532+
let exist: any Any.Type
533+
// CHECK-NOT: open_existential_expr {{.*}} location={{.*}}:[[@LINE+1]]:{{[0-9]+}} range=
534+
let result = foo(exist)
535+
do {
536+
var types = SwiftTypePair(typeOf: result, type2: SwiftType<any Any.Type>.self)
537+
types.assertTypesAreEqual()
538+
}
539+
}
540+
}

0 commit comments

Comments
 (0)