Skip to content

Commit

Permalink
Merge pull request swiftlang#78363 from AnthonyLatsis/dicksonia-arbor…
Browse files Browse the repository at this point in the history
…escens

[NFC] Sema: Tidy up `canOpenExistentialCallArgument`
  • Loading branch information
AnthonyLatsis authored Jan 2, 2025
2 parents 97b1548 + 56d943a commit 5c24df2
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 91 deletions.
9 changes: 5 additions & 4 deletions include/swift/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -3277,10 +3277,11 @@ class ConstraintSystem {
ConstraintLocator *getOpenOpaqueLocator(
ConstraintLocatorBuilder locator, OpaqueTypeDecl *opaqueDecl);

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

/// Update OpenedExistentials and record a change in the trail.
void recordOpenedExistentialType(ConstraintLocator *locator,
Expand Down
39 changes: 21 additions & 18 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1441,8 +1441,7 @@ class AllowLabelMismatches : public MatchCallArgumentListener {
}
};

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

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

OpenedArchetypeType *opened;
std::tie(argTy, opened) = cs.openExistentialType(
existentialType, cs.getConstraintLocator(loc));
OpenedArchetypeType *openedArchetype;

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

if (adjustments.contains(OpenedExistentialAdjustmentFlags::InOut))
argTy = InOutType::get(argTy);
return openedTy;
}

return std::nullopt;
});

openedExistentials.push_back({openedTypeVar, opened});
openedExistentials.push_back({typeVar, openedArchetype});
}

// If we have a compound function reference (e.g `fn($x:)`), respect
Expand Down Expand Up @@ -8638,7 +8641,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
if (type->isExistentialType()) {
if (auto elt = loc->getLastElementAs<LocatorPathElt::ContextualType>()) {
if (elt->getPurpose() == CTP_ForEachSequence) {
type = openExistentialType(type, loc).first;
type = openAnyExistentialType(type, loc).first;
}
}
}
Expand Down Expand Up @@ -12421,8 +12424,8 @@ ConstraintSystem::simplifyOpenedExistentialOfConstraint(
if (type2->isAnyExistentialType()) {
// We have the existential side. Produce an opened archetype and bind
// type1 to it.
Type openedTy = openExistentialType(type2, getConstraintLocator(locator))
.first;
Type openedTy =
openAnyExistentialType(type2, getConstraintLocator(locator)).first;
return matchTypes(type1, openedTy, ConstraintKind::Bind, subflags, locator);
}
if (!type2->isTypeVariableOrMember())
Expand Down
5 changes: 3 additions & 2 deletions lib/Sema/ConstraintSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -853,8 +853,9 @@ ConstraintLocator *ConstraintSystem::getOpenOpaqueLocator(
{ LocatorPathElt::OpenedOpaqueArchetype(opaqueDecl) }, 0);
}

std::pair<Type, OpenedArchetypeType *> ConstraintSystem::openExistentialType(
Type type, ConstraintLocator *locator) {
std::pair<Type, OpenedArchetypeType *>
ConstraintSystem::openAnyExistentialType(Type type,
ConstraintLocator *locator) {
Type result = OpenedArchetypeType::getAny(type);
Type t = result;
while (t->is<MetatypeType>())
Expand Down
86 changes: 39 additions & 47 deletions lib/Sema/OpenedExistentials.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -590,8 +590,7 @@ swift::isMemberAvailableOnExistential(Type baseTy, const ValueDecl *member) {
return result;
}

std::optional<
std::tuple<TypeVariableType *, Type, OpenedExistentialAdjustments>>
std::optional<std::pair<TypeVariableType *, Type>>
swift::canOpenExistentialCallArgument(ValueDecl *callee, unsigned paramIdx,
Type paramTy, Type argTy) {
if (!callee)
Expand Down Expand Up @@ -623,24 +622,6 @@ swift::canOpenExistentialCallArgument(ValueDecl *callee, unsigned paramIdx,
if (!paramTy->hasTypeVariable())
return std::nullopt;

OpenedExistentialAdjustments adjustments;

// The argument may be a "var" instead of a "let".
if (auto lv = argTy->getAs<LValueType>()) {
argTy = lv->getObjectType();
adjustments |= OpenedExistentialAdjustmentFlags::LValue;
}

// If the argument is inout, strip it off and we can add it back.
if (auto inOutArg = argTy->getAs<InOutType>()) {
argTy = inOutArg->getObjectType();
adjustments |= OpenedExistentialAdjustmentFlags::InOut;
}

// The argument type needs to be an existential type or metatype thereof.
if (!argTy->isAnyExistentialType())
return std::nullopt;

auto param = getParameterAt(callee, paramIdx);
if (!param)
return std::nullopt;
Expand All @@ -649,26 +630,40 @@ swift::canOpenExistentialCallArgument(ValueDecl *callee, unsigned paramIdx,
if (param->isVariadic())
return std::nullopt;

// Look through an inout and an optional type on the parameter types.
auto formalParamTy = param->getInterfaceType()->getInOutObjectType()
->lookThroughSingleOptionalType();
// Look through an inout and optional types on the parameter.
paramTy = paramTy->getInOutObjectType()->lookThroughSingleOptionalType();

// If the argument is of an existential metatype, look through the
// metatype on the parameter.
if (argTy->is<AnyMetatypeType>()) {
formalParamTy = formalParamTy->getMetatypeInstanceType();
paramTy = paramTy->getMetatypeInstanceType();
}

// The parameter type must be a type variable.
auto paramTypeVar = paramTy->getAs<TypeVariableType>();
if (!paramTypeVar)
// The rvalue argument type needs to be an existential type or metatype
// thereof.
const auto rValueArgTy = argTy->getWithoutSpecifierType();
if (!rValueArgTy->isAnyExistentialType())
return std::nullopt;

auto genericParam = formalParamTy->getAs<GenericTypeParamType>();
if (!genericParam)
GenericTypeParamType *genericParam;
TypeVariableType *typeVar;
Type bindingTy;

std::tie(genericParam, typeVar, bindingTy) = [=] {
// Look through an inout and optional type.
Type genericParam = param->getInterfaceType()
->getInOutObjectType()
->lookThroughSingleOptionalType();
Type typeVar =
paramTy->getInOutObjectType()->lookThroughSingleOptionalType();

Type bindingTy = rValueArgTy;

// Look through a metatype.
if (genericParam->is<AnyMetatypeType>()) {
genericParam = genericParam->getMetatypeInstanceType();
typeVar = typeVar->getMetatypeInstanceType();
bindingTy = bindingTy->getMetatypeInstanceType();
}

return std::tuple(genericParam->getAs<GenericTypeParamType>(),
typeVar->getAs<TypeVariableType>(), bindingTy);
}();

// The should have reached a type variable and corresponding generic
// parameter.
if (!typeVar || !genericParam)
return std::nullopt;

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

Type existentialTy;
if (auto existentialMetaTy = argTy->getAs<ExistentialMetatypeType>())
existentialTy = existentialMetaTy->getInstanceType();
else
existentialTy = argTy;

ASSERT(existentialTy->isAnyExistentialType());

// The binding could be an existential metatype. Get the instance type for
// conformance checks and to build an opened existential signature. If the
// instance type is not an existential type, i.e., the metatype is nested,
// bail out.
const Type existentialTy = bindingTy->getMetatypeInstanceType();
if (!existentialTy->isExistentialType())
return std::nullopt;

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

return std::make_tuple(paramTypeVar, argTy, adjustments);
return std::pair(typeVar, bindingTy);
}

/// For each occurrence of a type **type** in `refTy` that satisfies
Expand Down
27 changes: 7 additions & 20 deletions lib/Sema/OpenedExistentials.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,19 +132,10 @@ enum class ExistentialMemberAccessLimitation : uint8_t {
ExistentialMemberAccessLimitation
isMemberAvailableOnExistential(Type baseTy, const ValueDecl *member);

/// Flags that should be applied to the existential argument type after
/// opening.
enum class OpenedExistentialAdjustmentFlags {
/// The argument should be made inout after opening.
InOut = 0x01,
LValue = 0x02,
};

using OpenedExistentialAdjustments =
OptionSet<OpenedExistentialAdjustmentFlags>;

/// Determine whether we should open up the existential argument to the
/// given parameters.
/// Determine whether opening an existential argument for a function parameter
/// is supported.
/// A necessary condition for this is that the parameter interface type contains
/// a generic parameter type to which the opened argument can bind.
///
/// \param callee The function or subscript being called.
/// \param paramIdx The index specifying which function parameter is being
Expand All @@ -153,13 +144,9 @@ using OpenedExistentialAdjustments =
/// system.
/// \param argTy The type of the argument.
///
/// \returns If the argument type is existential and opening it can bind a
/// generic parameter in the callee, returns the type variable (from the opened
/// parameter type) the existential type that needs to be opened (from the
/// argument type), and the adjustments that need to be applied to the
/// existential type after it is opened.
std::optional<
std::tuple<TypeVariableType *, Type, OpenedExistentialAdjustments>>
/// \returns If opening is supported, returns the type variable representing the
/// generic parameter type, and the unopened type it binds to.
std::optional<std::pair<TypeVariableType *, Type>>
canOpenExistentialCallArgument(ValueDecl *callee, unsigned paramIdx,
Type paramTy, Type argTy);

Expand Down
40 changes: 40 additions & 0 deletions test/Constraints/opened_existentials.swift
Original file line number Diff line number Diff line change
Expand Up @@ -498,3 +498,43 @@ do {
// CHECK: open_existential_expr {{.*}} location={{.*}}:[[@LINE+1]]:{{[0-9]+}} range=
foo(&lValueP)
}

do {
do {
func foo<T : BitwiseCopyable>(_: T) -> T {}

let exist: any Any.Type
// CHECK: open_existential_expr {{.*}} location={{.*}}:[[@LINE+1]]:{{[0-9]+}} range=
let result = foo(exist)
do {
// FIXME: The result type should be 'any Any.Type'
// var types = SwiftTypePair(typeOf: result, type2: SwiftType<any Any.Type>.self)
var types = SwiftTypePair(typeOf: result, type2: SwiftType<Any>.self)
types.assertTypesAreEqual()
}
}

do {
func foo<T : BitwiseCopyable>(_: T) -> T {}

let exist: any Any.Type.Type
// CHECK-NOT: open_existential_expr {{.*}} location={{.*}}:[[@LINE+1]]:{{[0-9]+}} range=
let result = foo(exist)
do {
var types = SwiftTypePair(typeOf: result, type2: SwiftType<any Any.Type.Type>.self)
types.assertTypesAreEqual()
}
}

do {
func foo<T>(_: T) -> T {}

let exist: any Any.Type
// CHECK-NOT: open_existential_expr {{.*}} location={{.*}}:[[@LINE+1]]:{{[0-9]+}} range=
let result = foo(exist)
do {
var types = SwiftTypePair(typeOf: result, type2: SwiftType<any Any.Type>.self)
types.assertTypesAreEqual()
}
}
}

0 comments on commit 5c24df2

Please sign in to comment.