mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Sema: Fix crash with unresolved dot expression with optional protocol metatype base
Mostly fixes <rdar://problem/35945827>, but there is a case that should work that does not type check. At least we don't crash though.
This commit is contained in:
@@ -1204,7 +1204,16 @@ diagnoseTypeMemberOnInstanceLookup(Type baseObjTy,
|
|||||||
// to replace the metatype with 'Self'
|
// to replace the metatype with 'Self'
|
||||||
// error saying the lookup cannot be on a protocol metatype
|
// error saying the lookup cannot be on a protocol metatype
|
||||||
if (auto metatypeTy = baseObjTy->getAs<MetatypeType>()) {
|
if (auto metatypeTy = baseObjTy->getAs<MetatypeType>()) {
|
||||||
assert(metatypeTy->getInstanceType()->isExistentialType());
|
auto instanceTy = metatypeTy->getInstanceType();
|
||||||
|
|
||||||
|
// This will only happen if we have an unresolved dot expression
|
||||||
|
// (.foo) where foo is a protocol member and the contextual type is
|
||||||
|
// an optional protocol metatype.
|
||||||
|
if (auto objectTy = instanceTy->getAnyOptionalObjectType()) {
|
||||||
|
instanceTy = objectTy;
|
||||||
|
baseObjTy = MetatypeType::get(objectTy);
|
||||||
|
}
|
||||||
|
assert(instanceTy->isExistentialType());
|
||||||
|
|
||||||
// Give a customized message if we're accessing a member type
|
// Give a customized message if we're accessing a member type
|
||||||
// of a protocol -- otherwise a diagnostic talking about
|
// of a protocol -- otherwise a diagnostic talking about
|
||||||
@@ -1220,7 +1229,7 @@ diagnoseTypeMemberOnInstanceLookup(Type baseObjTy,
|
|||||||
} else if (isa<ConstructorDecl>(member)) {
|
} else if (isa<ConstructorDecl>(member)) {
|
||||||
Diag.emplace(diagnose(loc,
|
Diag.emplace(diagnose(loc,
|
||||||
diag::construct_protocol_by_name,
|
diag::construct_protocol_by_name,
|
||||||
metatypeTy->getInstanceType()));
|
instanceTy));
|
||||||
} else {
|
} else {
|
||||||
Diag.emplace(diagnose(loc,
|
Diag.emplace(diagnose(loc,
|
||||||
diag::could_not_use_type_member_on_protocol_metatype,
|
diag::could_not_use_type_member_on_protocol_metatype,
|
||||||
@@ -1234,8 +1243,7 @@ diagnoseTypeMemberOnInstanceLookup(Type baseObjTy,
|
|||||||
// If we are in a protocol extension of 'Proto' and we see
|
// If we are in a protocol extension of 'Proto' and we see
|
||||||
// 'Proto.static', suggest 'Self.static'
|
// 'Proto.static', suggest 'Self.static'
|
||||||
if (auto extensionContext = parent->getAsProtocolExtensionContext()) {
|
if (auto extensionContext = parent->getAsProtocolExtensionContext()) {
|
||||||
if (extensionContext->getDeclaredType()->isEqual(
|
if (extensionContext->getDeclaredType()->isEqual(instanceTy)) {
|
||||||
metatypeTy->getInstanceType())) {
|
|
||||||
Diag->fixItReplace(baseRange, "Self");
|
Diag->fixItReplace(baseRange, "Self");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2899,57 +2899,12 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
|
|||||||
ConstraintLocator *memberLocator,
|
ConstraintLocator *memberLocator,
|
||||||
bool includeInaccessibleMembers) {
|
bool includeInaccessibleMembers) {
|
||||||
Type baseObjTy = baseTy->getRValueType();
|
Type baseObjTy = baseTy->getRValueType();
|
||||||
|
|
||||||
// Dig out the instance type and figure out what members of the instance type
|
|
||||||
// we are going to see.
|
|
||||||
bool isMetatype = false;
|
|
||||||
bool isModule = false;
|
|
||||||
bool hasInstanceMembers = false;
|
|
||||||
bool hasInstanceMethods = false;
|
|
||||||
bool hasStaticMembers = false;
|
|
||||||
Type instanceTy = baseObjTy;
|
Type instanceTy = baseObjTy;
|
||||||
if (baseObjTy->is<ModuleType>()) {
|
|
||||||
hasStaticMembers = true;
|
|
||||||
isModule = true;
|
|
||||||
} else if (auto baseObjMeta = baseObjTy->getAs<AnyMetatypeType>()) {
|
|
||||||
instanceTy = baseObjMeta->getInstanceType();
|
|
||||||
isMetatype = true;
|
|
||||||
if (baseObjMeta->is<ExistentialMetatypeType>()) {
|
|
||||||
// An instance of an existential metatype is a concrete type conforming
|
|
||||||
// to the existential, say Self. Instance members of the concrete type
|
|
||||||
// have type Self -> T -> U, but we don't know what Self is at compile
|
|
||||||
// time so we cannot refer to them. Static methods are fine, on the other
|
|
||||||
// hand -- we already know that they do not have Self or associated type
|
|
||||||
// requirements, since otherwise we would not be able to refer to the
|
|
||||||
// existential metatype in the first place.
|
|
||||||
hasStaticMembers = true;
|
|
||||||
} else if (instanceTy->isExistentialType()) {
|
|
||||||
// A protocol metatype has instance methods with type P -> T -> U, but
|
|
||||||
// not instance properties or static members -- the metatype value itself
|
|
||||||
// doesn't give us a witness so there's no static method to bind.
|
|
||||||
hasInstanceMethods = true;
|
|
||||||
} else {
|
|
||||||
// Metatypes of nominal types and archetypes have instance methods and
|
|
||||||
// static members, but not instance properties.
|
|
||||||
// FIXME: partial application of properties
|
|
||||||
hasInstanceMethods = true;
|
|
||||||
hasStaticMembers = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we're at the root of an unevaluated context, we can
|
if (auto baseObjMeta = baseObjTy->getAs<AnyMetatypeType>()) {
|
||||||
// reference instance members on the metatype.
|
instanceTy = baseObjMeta->getInstanceType();
|
||||||
if (memberLocator &&
|
|
||||||
UnevaluatedRootExprs.count(memberLocator->getAnchor())) {
|
|
||||||
hasInstanceMembers = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Otherwise, we can access all instance members.
|
|
||||||
hasInstanceMembers = true;
|
|
||||||
hasInstanceMethods = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isExistential = instanceTy->isExistentialType();
|
|
||||||
|
|
||||||
if (instanceTy->isTypeVariableOrMember() ||
|
if (instanceTy->isTypeVariableOrMember() ||
|
||||||
instanceTy->is<UnresolvedType>()) {
|
instanceTy->is<UnresolvedType>()) {
|
||||||
MemberLookupResult result;
|
MemberLookupResult result;
|
||||||
@@ -3020,7 +2975,8 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
|
|||||||
|
|
||||||
/// Determine whether the given declaration has compatible argument
|
/// Determine whether the given declaration has compatible argument
|
||||||
/// labels.
|
/// labels.
|
||||||
auto hasCompatibleArgumentLabels = [&](ValueDecl *decl) -> bool {
|
auto hasCompatibleArgumentLabels = [&argumentLabels](Type baseObjTy,
|
||||||
|
ValueDecl *decl) -> bool {
|
||||||
if (!argumentLabels)
|
if (!argumentLabels)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@@ -3029,9 +2985,9 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
|
|||||||
// the member lookup binding the first level. But there are cases where
|
// the member lookup binding the first level. But there are cases where
|
||||||
// we can get an unapplied declaration reference back.
|
// we can get an unapplied declaration reference back.
|
||||||
unsigned parameterDepth;
|
unsigned parameterDepth;
|
||||||
if (isModule) {
|
if (baseObjTy->is<ModuleType>()) {
|
||||||
parameterDepth = 0;
|
parameterDepth = 0;
|
||||||
} else if (isMetatype && decl->isInstanceMember()) {
|
} else if (baseObjTy->is<AnyMetatypeType>() && decl->isInstanceMember()) {
|
||||||
parameterDepth = 0;
|
parameterDepth = 0;
|
||||||
} else {
|
} else {
|
||||||
parameterDepth = 1;
|
parameterDepth = 1;
|
||||||
@@ -3060,18 +3016,12 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The set of directly accessible types, which is only used when
|
|
||||||
// we're performing dynamic lookup into an existential type.
|
|
||||||
bool isDynamicLookup = instanceTy->isAnyObject();
|
|
||||||
|
|
||||||
// If the instance type is String bridged to NSString, compute
|
// If the instance type is String bridged to NSString, compute
|
||||||
// the type we'll look in for bridging.
|
// the type we'll look in for bridging.
|
||||||
Type bridgedClass;
|
|
||||||
Type bridgedType;
|
Type bridgedType;
|
||||||
if (instanceTy->getAnyNominal() == TC.Context.getStringDecl()) {
|
if (baseObjTy->getAnyNominal() == TC.Context.getStringDecl()) {
|
||||||
if (Type classType = TC.Context.getBridgedToObjC(DC, instanceTy)) {
|
if (Type classType = TC.Context.getBridgedToObjC(DC, instanceTy)) {
|
||||||
bridgedClass = classType;
|
bridgedType = classType;
|
||||||
bridgedType = isMetatype ? MetatypeType::get(classType) : classType;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bool labelMismatch = false;
|
bool labelMismatch = false;
|
||||||
@@ -3092,9 +3042,56 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
|
|||||||
if (!decl->hasInterfaceType())
|
if (!decl->hasInterfaceType())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Dig out the instance type and figure out what members of the instance type
|
||||||
|
// we are going to see.
|
||||||
|
auto baseTy = candidate.getBaseType();
|
||||||
|
auto baseObjTy = baseTy->getRValueType();
|
||||||
|
|
||||||
|
bool hasInstanceMembers = false;
|
||||||
|
bool hasInstanceMethods = false;
|
||||||
|
bool hasStaticMembers = false;
|
||||||
|
Type instanceTy = baseObjTy;
|
||||||
|
if (baseObjTy->is<ModuleType>()) {
|
||||||
|
hasStaticMembers = true;
|
||||||
|
} else if (auto baseObjMeta = baseObjTy->getAs<AnyMetatypeType>()) {
|
||||||
|
instanceTy = baseObjMeta->getInstanceType();
|
||||||
|
if (baseObjMeta->is<ExistentialMetatypeType>()) {
|
||||||
|
// An instance of an existential metatype is a concrete type conforming
|
||||||
|
// to the existential, say Self. Instance members of the concrete type
|
||||||
|
// have type Self -> T -> U, but we don't know what Self is at compile
|
||||||
|
// time so we cannot refer to them. Static methods are fine, on the other
|
||||||
|
// hand -- we already know that they do not have Self or associated type
|
||||||
|
// requirements, since otherwise we would not be able to refer to the
|
||||||
|
// existential metatype in the first place.
|
||||||
|
hasStaticMembers = true;
|
||||||
|
} else if (instanceTy->isExistentialType()) {
|
||||||
|
// A protocol metatype has instance methods with type P -> T -> U, but
|
||||||
|
// not instance properties or static members -- the metatype value itself
|
||||||
|
// doesn't give us a witness so there's no static method to bind.
|
||||||
|
hasInstanceMethods = true;
|
||||||
|
} else {
|
||||||
|
// Metatypes of nominal types and archetypes have instance methods and
|
||||||
|
// static members, but not instance properties.
|
||||||
|
// FIXME: partial application of properties
|
||||||
|
hasInstanceMethods = true;
|
||||||
|
hasStaticMembers = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're at the root of an unevaluated context, we can
|
||||||
|
// reference instance members on the metatype.
|
||||||
|
if (memberLocator &&
|
||||||
|
UnevaluatedRootExprs.count(memberLocator->getAnchor())) {
|
||||||
|
hasInstanceMembers = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Otherwise, we can access all instance members.
|
||||||
|
hasInstanceMembers = true;
|
||||||
|
hasInstanceMethods = true;
|
||||||
|
}
|
||||||
|
|
||||||
// If the argument labels for this result are incompatible with
|
// If the argument labels for this result are incompatible with
|
||||||
// the call site, skip it.
|
// the call site, skip it.
|
||||||
if (!hasCompatibleArgumentLabels(decl)) {
|
if (!hasCompatibleArgumentLabels(baseObjTy, decl)) {
|
||||||
labelMismatch = true;
|
labelMismatch = true;
|
||||||
result.addUnviable(candidate, MemberLookupResult::UR_LabelMismatch);
|
result.addUnviable(candidate, MemberLookupResult::UR_LabelMismatch);
|
||||||
return;
|
return;
|
||||||
@@ -3102,7 +3099,7 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
|
|||||||
|
|
||||||
// If our base is an existential type, we can't make use of any
|
// If our base is an existential type, we can't make use of any
|
||||||
// member whose signature involves associated types.
|
// member whose signature involves associated types.
|
||||||
if (isExistential) {
|
if (instanceTy->isExistentialType()) {
|
||||||
if (auto *proto = decl->getDeclContext()
|
if (auto *proto = decl->getDeclContext()
|
||||||
->getAsProtocolOrProtocolExtensionContext()) {
|
->getAsProtocolOrProtocolExtensionContext()) {
|
||||||
if (!proto->isAvailableInExistential(decl)) {
|
if (!proto->isAvailableInExistential(decl)) {
|
||||||
@@ -3141,10 +3138,9 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
|
|||||||
|
|
||||||
// If the underlying type of a typealias is fully concrete, it is legal
|
// If the underlying type of a typealias is fully concrete, it is legal
|
||||||
// to access the type with a protocol metatype base.
|
// to access the type with a protocol metatype base.
|
||||||
} else if (isExistential &&
|
} else if (instanceTy->isExistentialType() &&
|
||||||
isa<TypeAliasDecl>(decl) &&
|
isa<TypeAliasDecl>(decl) &&
|
||||||
!cast<TypeAliasDecl>(decl)->getInterfaceType()->getCanonicalType()
|
!cast<TypeAliasDecl>(decl)->getInterfaceType()->hasTypeParameter()) {
|
||||||
->hasTypeParameter()) {
|
|
||||||
|
|
||||||
/* We're OK */
|
/* We're OK */
|
||||||
|
|
||||||
@@ -3158,8 +3154,9 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
|
|||||||
|
|
||||||
// If we have an rvalue base, make sure that the result isn't 'mutating'
|
// If we have an rvalue base, make sure that the result isn't 'mutating'
|
||||||
// (only valid on lvalues).
|
// (only valid on lvalues).
|
||||||
if (!isMetatype &&
|
if (!baseTy->is<AnyMetatypeType>() &&
|
||||||
!baseTy->is<LValueType>() && decl->isInstanceMember()) {
|
!baseTy->is<LValueType>() &&
|
||||||
|
decl->isInstanceMember()) {
|
||||||
if (auto *FD = dyn_cast<FuncDecl>(decl))
|
if (auto *FD = dyn_cast<FuncDecl>(decl))
|
||||||
if (FD->isMutating()) {
|
if (FD->isMutating()) {
|
||||||
result.addUnviable(candidate,
|
result.addUnviable(candidate,
|
||||||
@@ -3188,7 +3185,7 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
|
|||||||
bool isUnwrappedOptional) -> OverloadChoice {
|
bool isUnwrappedOptional) -> OverloadChoice {
|
||||||
// If we're looking into an existential type, check whether this
|
// If we're looking into an existential type, check whether this
|
||||||
// result was found via dynamic lookup.
|
// result was found via dynamic lookup.
|
||||||
if (isDynamicLookup) {
|
if (instanceTy->isAnyObject()) {
|
||||||
assert(cand->getDeclContext()->isTypeContext() && "Dynamic lookup bug");
|
assert(cand->getDeclContext()->isTypeContext() && "Dynamic lookup bug");
|
||||||
|
|
||||||
// We found this declaration via dynamic lookup, record it as such.
|
// We found this declaration via dynamic lookup, record it as such.
|
||||||
@@ -3202,9 +3199,8 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
|
|||||||
|
|
||||||
// If we got the choice by unwrapping an optional type, unwrap the base
|
// If we got the choice by unwrapping an optional type, unwrap the base
|
||||||
// type.
|
// type.
|
||||||
Type ovlBaseTy = baseTy;
|
|
||||||
if (isUnwrappedOptional) {
|
if (isUnwrappedOptional) {
|
||||||
ovlBaseTy = MetatypeType::get(baseTy->castTo<MetatypeType>()
|
auto ovlBaseTy = MetatypeType::get(baseTy->castTo<MetatypeType>()
|
||||||
->getInstanceType()
|
->getInstanceType()
|
||||||
->getAnyOptionalObjectType());
|
->getAnyOptionalObjectType());
|
||||||
return OverloadChoice::getDeclViaUnwrappedOptional(ovlBaseTy, cand,
|
return OverloadChoice::getDeclViaUnwrappedOptional(ovlBaseTy, cand,
|
||||||
@@ -3216,7 +3212,7 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
|
|||||||
baseTy, cand, functionRefKind);
|
baseTy, cand, functionRefKind);
|
||||||
}
|
}
|
||||||
|
|
||||||
return OverloadChoice(ovlBaseTy, cand, functionRefKind);
|
return OverloadChoice(baseTy, cand, functionRefKind);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add all results from this lookup.
|
// Add all results from this lookup.
|
||||||
@@ -3229,8 +3225,8 @@ retry_after_fail:
|
|||||||
|
|
||||||
// If the instance type is a bridged to an Objective-C type, perform
|
// If the instance type is a bridged to an Objective-C type, perform
|
||||||
// a lookup into that Objective-C type.
|
// a lookup into that Objective-C type.
|
||||||
if (bridgedType && !isMetatype) {
|
if (bridgedType) {
|
||||||
LookupResult &bridgedLookup = lookupMember(bridgedClass, memberName);
|
LookupResult &bridgedLookup = lookupMember(bridgedType, memberName);
|
||||||
ModuleDecl *foundationModule = nullptr;
|
ModuleDecl *foundationModule = nullptr;
|
||||||
for (auto result : bridgedLookup) {
|
for (auto result : bridgedLookup) {
|
||||||
// Ignore results from the Objective-C "Foundation"
|
// Ignore results from the Objective-C "Foundation"
|
||||||
@@ -3258,7 +3254,8 @@ retry_after_fail:
|
|||||||
// through optional types.
|
// through optional types.
|
||||||
//
|
//
|
||||||
// FIXME: The short-circuit here is lame.
|
// FIXME: The short-circuit here is lame.
|
||||||
if (result.ViableCandidates.empty() && isMetatype &&
|
if (result.ViableCandidates.empty() &&
|
||||||
|
baseObjTy->is<AnyMetatypeType>() &&
|
||||||
constraintKind == ConstraintKind::UnresolvedValueMember) {
|
constraintKind == ConstraintKind::UnresolvedValueMember) {
|
||||||
if (auto objectType = instanceTy->getAnyOptionalObjectType()) {
|
if (auto objectType = instanceTy->getAnyOptionalObjectType()) {
|
||||||
if (objectType->mayHaveMembers()) {
|
if (objectType->mayHaveMembers()) {
|
||||||
|
|||||||
@@ -67,3 +67,24 @@ func nestedOptContext() -> Foo?? {
|
|||||||
return .none
|
return .none
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// <rdar://problem/35945827>
|
||||||
|
|
||||||
|
// This should diagnose instead of crashing in SILGen
|
||||||
|
protocol Horse {
|
||||||
|
static var palomino: Horse { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
func rideAHorse(_ horse: Horse?) {}
|
||||||
|
|
||||||
|
rideAHorse(.palomino)
|
||||||
|
// expected-error@-1 {{static member 'palomino' cannot be used on protocol metatype 'Horse.Protocol'}}
|
||||||
|
|
||||||
|
// FIXME: This should work if the static member is part of a class though
|
||||||
|
class Donkey {
|
||||||
|
static var mule: Donkey & Horse { while true {} }
|
||||||
|
}
|
||||||
|
|
||||||
|
func rideAMule(_ mule: (Horse & Donkey)?) {}
|
||||||
|
|
||||||
|
rideAMule(.mule)
|
||||||
|
// expected-error@-1 {{static member 'mule' cannot be used on protocol metatype '(Donkey & Horse).Protocol'}}
|
||||||
|
|||||||
Reference in New Issue
Block a user