Skip to content

Commit b05678f

Browse files
committed
[ConstraintSystem] Allow static member references on protocol metatypes
1 parent 6189e8e commit b05678f

File tree

3 files changed

+88
-12
lines changed

3 files changed

+88
-12
lines changed

lib/Sema/CSApply.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,6 +1166,50 @@ namespace {
11661166
baseIsInstance = false;
11671167
isExistentialMetatype = baseMeta->is<ExistentialMetatypeType>();
11681168
baseTy = baseMeta->getInstanceType();
1169+
1170+
// A valid reference to a static member (computed property or a method)
1171+
// declared on a protocol is only possible if result type conforms to
1172+
// that protocol, otherwise it would be impossible to find a witness to
1173+
// use.
1174+
// Such means that (for valid references) base expression here could be
1175+
// adjusted to point to a type conforming to a protocol as-if reference
1176+
// has originated directly from it e.g.
1177+
//
1178+
// \code
1179+
// protocol P {}
1180+
// struct S : P {}
1181+
//
1182+
// extension P {
1183+
// static var foo: S { S() }
1184+
// }
1185+
//
1186+
// _ = P.foo
1187+
// \endcode
1188+
//
1189+
// Here `P.foo` would be replaced with `S.foo`
1190+
if (!isExistentialMetatype && baseTy->is<ProtocolType>() &&
1191+
member->isStatic()) {
1192+
auto refKind = choice.getFunctionRefKind();
1193+
Type baseTy;
1194+
1195+
bool isMethod = isa<AbstractFunctionDecl>(member);
1196+
switch (refKind) {
1197+
case FunctionRefKind::Compound:
1198+
case FunctionRefKind::Unapplied:
1199+
case FunctionRefKind::SingleApply: {
1200+
baseTy = isMethod ? openedType->castTo<FunctionType>()->getResult()
1201+
: openedType;
1202+
break;
1203+
}
1204+
1205+
case FunctionRefKind::DoubleApply: {
1206+
llvm_unreachable("not implemented yet");
1207+
}
1208+
}
1209+
1210+
base = TypeExpr::createImplicitHack(base->getLoc(), baseTy, context);
1211+
cs.cacheType(base);
1212+
}
11691213
}
11701214

11711215
// Build a member reference.

lib/Sema/CSSimplify.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6711,6 +6711,7 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
67116711
// not instance properties or static members -- the metatype value itself
67126712
// doesn't give us a witness so there's no static method to bind.
67136713
hasInstanceMethods = true;
6714+
hasStaticMembers = true;
67146715
} else {
67156716
// Metatypes of nominal types and archetypes have instance methods and
67166717
// static members, but not instance properties.

lib/Sema/ConstraintSystem.cpp

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1453,20 +1453,26 @@ ConstraintSystem::getTypeOfMemberReference(
14531453
const DeclRefExpr *base,
14541454
OpenedTypeMap *replacementsPtr) {
14551455
// Figure out the instance type used for the base.
1456-
Type baseObjTy = getFixedTypeRecursive(baseTy, /*wantRValue=*/true);
1456+
Type resolvedBaseTy = getFixedTypeRecursive(baseTy, /*wantRValue=*/true);
14571457

14581458
// If the base is a module type, just use the type of the decl.
1459-
if (baseObjTy->is<ModuleType>()) {
1459+
if (resolvedBaseTy->is<ModuleType>()) {
14601460
return getTypeOfReference(value, functionRefKind, locator, useDC);
14611461
}
14621462

14631463
// Check to see if the self parameter is applied, in which case we'll want to
14641464
// strip it off later.
1465-
auto hasAppliedSelf = doesMemberRefApplyCurriedSelf(baseObjTy, value);
1465+
auto hasAppliedSelf = doesMemberRefApplyCurriedSelf(resolvedBaseTy, value);
14661466

1467-
baseObjTy = baseObjTy->getMetatypeInstanceType();
1467+
auto baseObjTy = resolvedBaseTy->getMetatypeInstanceType();
14681468
FunctionType::Param baseObjParam(baseObjTy);
14691469

1470+
// Indicates whether this is a reference to a static member on a protocol
1471+
// metatype e.g. existential metatype.
1472+
bool isStaticMemberRefOnProtocol = resolvedBaseTy->is<MetatypeType>() &&
1473+
baseObjTy->isExistentialType() &&
1474+
value->isStatic();
1475+
14701476
if (auto *typeDecl = dyn_cast<TypeDecl>(value)) {
14711477
assert(!isa<ModuleDecl>(typeDecl) && "Nested module?");
14721478

@@ -1569,10 +1575,28 @@ ConstraintSystem::getTypeOfMemberReference(
15691575

15701576
// If we are looking at a member of an existential, open the existential.
15711577
Type baseOpenedTy = baseObjTy;
1572-
if (baseObjTy->isExistentialType()) {
1578+
1579+
if (isStaticMemberRefOnProtocol) {
1580+
auto refTy = openedType->castTo<FunctionType>();
1581+
switch (functionRefKind) {
1582+
// Either variable or use of method as a value
1583+
case FunctionRefKind::Unapplied:
1584+
case FunctionRefKind::SingleApply:
1585+
case FunctionRefKind::Compound: {
1586+
baseOpenedTy =
1587+
isa<AbstractFunctionDecl>(value)
1588+
? refTy->getResult()->castTo<FunctionType>()->getResult()
1589+
: refTy->getResult();
1590+
break;
1591+
}
1592+
1593+
case FunctionRefKind::DoubleApply:
1594+
llvm_unreachable("not implemented yet");
1595+
}
1596+
} else if (baseObjTy->isExistentialType()) {
15731597
auto openedArchetype = OpenedArchetypeType::get(baseObjTy);
1574-
OpenedExistentialTypes.push_back({ getConstraintLocator(locator),
1575-
openedArchetype });
1598+
OpenedExistentialTypes.push_back(
1599+
{getConstraintLocator(locator), openedArchetype});
15761600
baseOpenedTy = openedArchetype;
15771601
}
15781602

@@ -1582,11 +1606,18 @@ ConstraintSystem::getTypeOfMemberReference(
15821606

15831607
Type selfObjTy = openedParams.front().getPlainType()->getMetatypeInstanceType();
15841608
if (outerDC->getSelfProtocolDecl()) {
1585-
// For a protocol, substitute the base object directly. We don't need a
1586-
// conformance constraint because we wouldn't have found the declaration
1587-
// if it didn't conform.
1588-
addConstraint(ConstraintKind::Bind, baseOpenedTy, selfObjTy,
1589-
getConstraintLocator(locator));
1609+
if (isStaticMemberRefOnProtocol) {
1610+
// If this is a static member reference on a protocol metatype
1611+
// we need to make sure that base type, we yet to infer from a
1612+
// result type of the member, does indeed conform to `Self`.
1613+
addSelfConstraint(*this, baseOpenedTy, selfObjTy, locator);
1614+
} else {
1615+
// For a protocol, substitute the base object directly. We don't need a
1616+
// conformance constraint because we wouldn't have found the declaration
1617+
// if it didn't conform.
1618+
addConstraint(ConstraintKind::Bind, baseOpenedTy, selfObjTy,
1619+
getConstraintLocator(locator));
1620+
}
15901621
} else if (!isDynamicResult) {
15911622
addSelfConstraint(*this, baseOpenedTy, selfObjTy, locator);
15921623
}

0 commit comments

Comments
 (0)