Skip to content

Commit c44a92a

Browse files
committed
[ConstraintSystem] Implement new rule for static member lookup in generic contexts
Instead of requiring result type of the member to conform to declaring protocol, as originally proposed by SE-0299, let's instead require `Self` to be bound to some concrete type in extension that declares a static member. This would make sure that: 1. Members are only visible on a particular type; 2. There is no ambiguity regarding what base of the member chain is going to be.
1 parent 55cd99b commit c44a92a

File tree

4 files changed

+50
-57
lines changed

4 files changed

+50
-57
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5539,6 +5539,11 @@ bool hasExplicitResult(ClosureExpr *closure);
55395539
void performSyntacticDiagnosticsForTarget(
55405540
const SolutionApplicationTarget &target, bool isExprStmt);
55415541

5542+
/// Given a member of a protocol, check whether `Self` type of that
5543+
/// protocol is contextually bound to some concrete type via same-type
5544+
/// generic requirement and if so return that type or null type otherwise.
5545+
Type getConcreteReplacementForProtocolSelfType(ValueDecl *member);
5546+
55425547
} // end namespace constraints
55435548

55445549
template<typename ...Args>

lib/Sema/CSSimplify.cpp

Lines changed: 6 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6852,9 +6852,10 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
68526852
/* We're OK */
68536853
} else if (hasStaticMembers && baseObjTy->is<MetatypeType>() &&
68546854
instanceTy->isExistentialType()) {
6855-
// Static member lookup on protocol metatype requires that result type
6856-
// of a selected member to conform to protocol this method is being
6857-
// referred from.
6855+
// Static member lookup on protocol metatype in generic context
6856+
// requires `Self` of the protocol to be bound to some concrete
6857+
// type via same-type requirement, otherwise it would be
6858+
// impossible to find a witness for this member.
68586859

68596860
// Cannot instantiate a protocol or reference a member on
68606861
// protocol composition type.
@@ -6865,40 +6866,13 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
68656866
return;
68666867
}
68676868

6868-
Type resultTy;
6869-
6870-
if (isa<AbstractFunctionDecl>(decl)) {
6871-
auto refTy =
6872-
decl->getInterfaceType()->castTo<AnyFunctionType>()->getResult();
6873-
resultTy = refTy->castTo<FunctionType>()->getResult();
6874-
} else if (isa<SubscriptDecl>(decl)) {
6875-
resultTy =
6876-
decl->getInterfaceType()->castTo<AnyFunctionType>()->getResult();
6869+
if (getConcreteReplacementForProtocolSelfType(decl)) {
6870+
result.addViable(candidate);
68776871
} else {
6878-
resultTy = decl->getInterfaceType();
6879-
}
6880-
6881-
if (auto *fnType = resultTy->getAs<FunctionType>())
6882-
resultTy = fnType->getResult();
6883-
6884-
// No reason to suggest that `Void` doesn't conform to some protocol.
6885-
if (resultTy->isVoid()) {
68866872
result.addUnviable(candidate,
68876873
MemberLookupResult::UR_TypeMemberOnInstance);
6888-
return;
6889-
}
6890-
6891-
// If result is not a concrete type which could conform to the
6892-
// expected protocol, this method is only viable for diagnostics.
6893-
if (resultTy->isExistentialType() || resultTy->is<AnyFunctionType>() ||
6894-
resultTy->is<TupleType>() || resultTy->is<AnyMetatypeType>()) {
6895-
result.addUnviable(
6896-
candidate,
6897-
MemberLookupResult::UR_InvalidStaticMemberOnProtocolMetatype);
6898-
return;
68996874
}
69006875

6901-
result.addViable(candidate);
69026876
return;
69036877
} else {
69046878
if (!hasStaticMembers) {

lib/Sema/ConstraintSystem.cpp

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1589,19 +1589,12 @@ ConstraintSystem::getTypeOfMemberReference(
15891589
// reference e.g. it might be incorrect initializer call
15901590
// or result type is invalid.
15911591
if (!(shouldAttemptFixes() && hasFixFor(getConstraintLocator(locator)))) {
1592-
// Member type with Self applied.
1593-
auto refTy = openedType->castTo<FunctionType>()->getResult();
1594-
// If member is a function type, let's use its result type
1595-
// since it could be either a static method or a property
1596-
// which returns a function type.
1597-
if (auto *funcTy = refTy->getAs<FunctionType>())
1598-
baseOpenedTy = funcTy->getResult();
1599-
else
1600-
baseOpenedTy = refTy;
1601-
1602-
// It should be possible to form optional chains which start
1603-
// from a protocol metatype.
1604-
baseOpenedTy = baseOpenedTy->lookThroughAllOptionalTypes();
1592+
if (auto concreteSelf =
1593+
getConcreteReplacementForProtocolSelfType(value)) {
1594+
// Concrete type replacing `Self` could be generic, so we need
1595+
// to make sure that it's opened before use.
1596+
baseOpenedTy = openType(concreteSelf, replacements);
1597+
}
16051598
}
16061599
} else if (baseObjTy->isExistentialType()) {
16071600
auto openedArchetype = OpenedArchetypeType::get(baseObjTy);
@@ -4540,6 +4533,23 @@ bool constraints::hasExplicitResult(ClosureExpr *closure) {
45404533
ClosureHasExplicitResultRequest{closure}, false);
45414534
}
45424535

4536+
Type constraints::getConcreteReplacementForProtocolSelfType(ValueDecl *member) {
4537+
auto *DC = member->getDeclContext();
4538+
4539+
if (!DC->getSelfProtocolDecl())
4540+
return Type();
4541+
4542+
GenericSignature signature;
4543+
if (auto *genericContext = member->getAsGenericContext()) {
4544+
signature = genericContext->getGenericSignature();
4545+
} else {
4546+
signature = DC->getGenericSignatureOfContext();
4547+
}
4548+
4549+
auto selfTy = DC->getProtocolSelfType();
4550+
return signature->getConcreteType(selfTy);
4551+
}
4552+
45434553
static bool isOperator(Expr *expr, StringRef expectedName) {
45444554
auto name = getOperatorName(expr);
45454555
return name ? name->is(expectedName) : false;

test/Constraints/static_members_on_protocol_metatype.swift renamed to test/Constraints/static_members_on_protocol_in_generic_context.swift

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ struct G<T> : P {
1111
var other: G<T> { fatalError() }
1212
}
1313

14-
extension P {
14+
extension P where Self == S {
1515
static var property: S { S() }
1616

1717
static var iuoProp: S! { S() }
@@ -25,15 +25,17 @@ extension P {
2525
return S()
2626
}
2727

28-
static func genericFn<T>(_: T) -> G<T> {
29-
return G<T>()
30-
}
31-
3228
static subscript(_: Int) -> S {
3329
get { S() }
3430
}
31+
}
32+
33+
extension P {
34+
static func genericFn<T>(_: T) -> G<T> where Self == G<T> {
35+
return G<T>()
36+
}
3537

36-
static subscript<T>(t t: T) -> G<T> {
38+
static subscript<T>(t t: T) -> G<T> where Self == G<T> {
3739
get { G<T>() }
3840
}
3941
}
@@ -107,7 +109,7 @@ protocol Q {}
107109

108110
func test_combo<T: P & Q>(_: T) {} // expected-note 2 {{where 'T' = 'G<Int>'}}
109111

110-
extension Q {
112+
extension Q where Self == S {
111113
static var otherProperty: S { S() }
112114

113115
static func otherMethod() -> S {
@@ -136,14 +138,16 @@ test_combo(.genericFn(42)) // expected-error {{global function 'test_combo' requ
136138
/* Invalid result types */
137139

138140
extension P {
139-
static var invalidProp: Int { 42 } // expected-note 3 {{'invalidProp' declared here}}
140-
static var selfProp: Self { fatalError() }
141-
static func invalidMethod() -> Int { 42 } // expected-note 3 {{'invalidMethod()' declared here}}
142-
static func generic<T>(_: T) -> T { fatalError() } // expected-note 3 {{'generic' declared here}}
143-
static func genericWithReqs<T: Collection, Q>(_: T) -> Q where T.Element == Q { // expected-note {{in call to function 'genericWithReqs'}} expected-note 2 {{'genericWithReqs' declared here}} expected-note 3 {{required by static method 'genericWithReqs' where 'T' = '()'}}
141+
static func generic<T>(_: T) -> T where Self == T { fatalError() } // expected-note 3 {{'generic' declared here}}
142+
static func genericWithReqs<T: Collection, Q>(_: T) -> Q where T.Element == Q, Self == Q { // expected-note {{in call to function 'genericWithReqs'}} expected-note 2 {{'genericWithReqs' declared here}} expected-note 3 {{required by static method 'genericWithReqs' where 'T' = '()'}}
144143
fatalError()
145144
}
145+
}
146146

147+
extension P {
148+
static var invalidProp: Int { 42 } // expected-note 3 {{'invalidProp' declared here}}
149+
static var selfProp: Self { fatalError() }
150+
static func invalidMethod() -> Int { 42 } // expected-note 3 {{'invalidMethod()' declared here}}
147151
static subscript(q q: String) -> Int { get { 42 } }
148152
}
149153

0 commit comments

Comments
 (0)