Skip to content

Commit dee8d7b

Browse files
authored
Merge pull request #23274 from xedin/validate-refs-on-creation
[ConstraintSystem] Attach fixes to incorrectly referenced or unviable…
2 parents 547eea5 + 7633d01 commit dee8d7b

File tree

3 files changed

+277
-231
lines changed

3 files changed

+277
-231
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 215 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3980,6 +3980,191 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
39803980
return result;
39813981
}
39823982

3983+
/// Determine whether the given type refers to a non-final class (or
3984+
/// dynamic self of one).
3985+
static bool isNonFinalClass(Type type) {
3986+
if (auto dynamicSelf = type->getAs<DynamicSelfType>())
3987+
type = dynamicSelf->getSelfType();
3988+
3989+
if (auto classDecl = type->getClassOrBoundGenericClass())
3990+
return !classDecl->isFinal();
3991+
3992+
if (auto archetype = type->getAs<ArchetypeType>())
3993+
if (auto super = archetype->getSuperclass())
3994+
return isNonFinalClass(super);
3995+
3996+
return type->isExistentialType();
3997+
}
3998+
3999+
/// Determine whether given constructor reference is valid or does it require
4000+
/// any fixes e.g. when base is a protocol metatype.
4001+
static ConstraintFix *validateInitializerRef(ConstraintSystem &cs,
4002+
ConstructorDecl *init,
4003+
ConstraintLocator *locator) {
4004+
auto *anchor = locator->getAnchor();
4005+
if (!anchor)
4006+
return nullptr;
4007+
4008+
auto getType = [&cs](const Expr *expr) -> Type {
4009+
return cs.simplifyType(cs.getType(expr))->getRValueType();
4010+
};
4011+
4012+
auto locatorEndsWith =
4013+
[](ConstraintLocator *locator,
4014+
ConstraintLocator::PathElementKind eltKind) -> bool {
4015+
auto path = locator->getPath();
4016+
return !path.empty() && path.back().getKind() == eltKind;
4017+
};
4018+
4019+
Expr *baseExpr = nullptr;
4020+
Type baseType;
4021+
4022+
// Explicit initializer reference e.g. `T.init(...)` or `T.init`.
4023+
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(anchor)) {
4024+
baseExpr = UDE->getBase();
4025+
baseType = getType(baseExpr);
4026+
if (baseType->is<MetatypeType>()) {
4027+
auto instanceType = baseType->getAs<MetatypeType>()
4028+
->getInstanceType()
4029+
->getWithoutParens();
4030+
if (!cs.isTypeReference(baseExpr) && instanceType->isExistentialType()) {
4031+
return AllowInvalidInitRef::onProtocolMetatype(
4032+
cs, baseType, init, /*isStaticallyDerived=*/true,
4033+
baseExpr->getSourceRange(), locator);
4034+
}
4035+
}
4036+
// Initializer call e.g. `T(...)`
4037+
} else if (auto *CE = dyn_cast<CallExpr>(anchor)) {
4038+
baseExpr = CE->getFn();
4039+
baseType = getType(baseExpr);
4040+
// If this is an initializer call without explicit mention
4041+
// of `.init` on metatype value.
4042+
if (auto *AMT = baseType->getAs<AnyMetatypeType>()) {
4043+
auto instanceType = AMT->getInstanceType()->getWithoutParens();
4044+
if (!cs.isTypeReference(baseExpr)) {
4045+
if (baseType->is<MetatypeType>() &&
4046+
instanceType->isAnyExistentialType()) {
4047+
return AllowInvalidInitRef::onProtocolMetatype(
4048+
cs, baseType, init, cs.isStaticallyDerivedMetatype(baseExpr),
4049+
baseExpr->getSourceRange(), locator);
4050+
}
4051+
4052+
if (!instanceType->isExistentialType() ||
4053+
instanceType->isAnyExistentialType()) {
4054+
return AllowInvalidInitRef::onNonConstMetatype(cs, baseType, init,
4055+
locator);
4056+
}
4057+
}
4058+
}
4059+
// Initializer reference which requires contextual base type e.g.
4060+
// `.init(...)`.
4061+
} else if (auto *UME = dyn_cast<UnresolvedMemberExpr>(anchor)) {
4062+
// We need to find type variable which represents contextual base.
4063+
auto *baseLocator = cs.getConstraintLocator(
4064+
UME, locatorEndsWith(locator, ConstraintLocator::ConstructorMember)
4065+
? ConstraintLocator::UnresolvedMember
4066+
: ConstraintLocator::MemberRefBase);
4067+
4068+
// FIXME: Type variables responsible for contextual base could be cached
4069+
// in the constraint system to speed up lookup.
4070+
auto result = llvm::find_if(
4071+
cs.getTypeVariables(), [&baseLocator](const TypeVariableType *typeVar) {
4072+
return typeVar->getImpl().getLocator() == baseLocator;
4073+
});
4074+
4075+
assert(result != cs.getTypeVariables().end());
4076+
baseType = cs.simplifyType(*result)->getRValueType();
4077+
// Constraint for member base is formed as '$T.Type[.<member] = ...`
4078+
// which means MetatypeType has to be added after finding a type variable.
4079+
if (locatorEndsWith(baseLocator, ConstraintLocator::MemberRefBase))
4080+
baseType = MetatypeType::get(baseType);
4081+
}
4082+
4083+
if (!baseType)
4084+
return nullptr;
4085+
4086+
if (!baseType->is<AnyMetatypeType>()) {
4087+
bool applicable = false;
4088+
// Special case -- in a protocol extension initializer with a class
4089+
// constrainted Self type, 'self' has archetype type, and only
4090+
// required initializers can be called.
4091+
if (baseExpr && !baseExpr->isSuperExpr()) {
4092+
auto &ctx = cs.getASTContext();
4093+
if (auto *DRE =
4094+
dyn_cast<DeclRefExpr>(baseExpr->getSemanticsProvidingExpr())) {
4095+
if (DRE->getDecl()->getFullName() == ctx.Id_self) {
4096+
if (getType(DRE)->is<ArchetypeType>())
4097+
applicable = true;
4098+
}
4099+
}
4100+
}
4101+
4102+
if (!applicable)
4103+
return nullptr;
4104+
}
4105+
4106+
auto instanceType = baseType->getMetatypeInstanceType();
4107+
bool isStaticallyDerived = true;
4108+
// If this is expression like `.init(...)` where base type is
4109+
// determined by a contextual type.
4110+
if (!baseExpr) {
4111+
isStaticallyDerived = !(instanceType->is<DynamicSelfType>() ||
4112+
instanceType->is<ArchetypeType>());
4113+
// Otherwise this is something like `T.init(...)`
4114+
} else {
4115+
isStaticallyDerived = cs.isStaticallyDerivedMetatype(baseExpr);
4116+
}
4117+
4118+
auto baseRange = baseExpr ? baseExpr->getSourceRange() : SourceRange();
4119+
// FIXME: The "hasClangNode" check here is a complete hack.
4120+
if (isNonFinalClass(instanceType) && !isStaticallyDerived &&
4121+
!init->hasClangNode() &&
4122+
!(init->isRequired() || init->getDeclContext()->getSelfProtocolDecl())) {
4123+
return AllowInvalidInitRef::dynamicOnMetatype(cs, baseType, init, baseRange,
4124+
locator);
4125+
// Constructors cannot be called on a protocol metatype, because there is no
4126+
// metatype to witness it.
4127+
} else if (baseType->is<MetatypeType>() &&
4128+
instanceType->isExistentialType()) {
4129+
return AllowInvalidInitRef::onProtocolMetatype(
4130+
cs, baseType, init, isStaticallyDerived, baseRange, locator);
4131+
}
4132+
4133+
return nullptr;
4134+
}
4135+
4136+
static ConstraintFix *
4137+
fixMemberRef(ConstraintSystem &cs, Type baseTy,
4138+
DeclName memberName, const OverloadChoice &choice,
4139+
ConstraintLocator *locator,
4140+
Optional<MemberLookupResult::UnviableReason> reason = None) {
4141+
if (!choice.isDecl())
4142+
return nullptr;
4143+
4144+
auto *decl = choice.getDecl();
4145+
if (auto *CD = dyn_cast<ConstructorDecl>(decl)) {
4146+
if (auto *fix = validateInitializerRef(cs, CD, locator))
4147+
return fix;
4148+
}
4149+
4150+
if (reason) {
4151+
switch (*reason) {
4152+
case MemberLookupResult::UR_InstanceMemberOnType:
4153+
case MemberLookupResult::UR_TypeMemberOnInstance:
4154+
return AllowTypeOrInstanceMember::create(cs, baseTy, memberName, locator);
4155+
4156+
case MemberLookupResult::UR_MutatingMemberOnRValue:
4157+
case MemberLookupResult::UR_MutatingGetterOnRValue:
4158+
case MemberLookupResult::UR_LabelMismatch:
4159+
case MemberLookupResult::UR_Inaccessible:
4160+
case MemberLookupResult::UR_UnavailableInExistential:
4161+
break;
4162+
}
4163+
}
4164+
4165+
return nullptr;
4166+
}
4167+
39834168
ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
39844169
ConstraintKind kind, Type baseTy, DeclName member, Type memberTy,
39854170
DeclContext *useDC, FunctionRefKind functionRefKind,
@@ -4021,8 +4206,12 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
40214206
// If we found viable candidates, then we're done!
40224207
if (!result.ViableCandidates.empty()) {
40234208
llvm::SmallVector<Constraint *, 8> candidates;
4024-
generateConstraints(candidates, memberTy, result.ViableCandidates,
4025-
useDC, locator, result.getFavoredChoice());
4209+
generateConstraints(
4210+
candidates, memberTy, result.ViableCandidates, useDC, locator,
4211+
result.getFavoredIndex(), /*requiresFix=*/false,
4212+
[&](unsigned, const OverloadChoice &choice) {
4213+
return fixMemberRef(*this, baseTy, member, choice, locator);
4214+
});
40264215

40274216
if (!outerAlternatives.empty()) {
40284217
// If local scope has a single choice,
@@ -4049,46 +4238,26 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
40494238
if (!MissingMembers.insert(locator))
40504239
return SolutionKind::Error;
40514240

4052-
// FIXME(diagnostics): If there were no viable results, but there are
4053-
// unviable ones, we'd have to introduce fix for each specific problem.
40544241
if (!result.UnviableCandidates.empty()) {
4055-
// Check if we have unviable candidates whose reason for rejection
4056-
// is either UR_InstanceMemberOnType or UR_TypeMemberOnInstance
4057-
SmallVector<OverloadChoice, 4> filteredCandidates;
4058-
4059-
llvm::for_each(
4060-
result.UnviableCandidates,
4061-
[&](const std::pair<OverloadChoice,
4062-
MemberLookupResult::UnviableReason> &c) {
4063-
if (c.second == MemberLookupResult::UR_InstanceMemberOnType ||
4064-
c.second == MemberLookupResult::UR_TypeMemberOnInstance) {
4065-
filteredCandidates.push_back(std::move(c.first));
4066-
}
4067-
});
4068-
4069-
// If we do, then allow them
4070-
if (!filteredCandidates.empty()) {
4071-
4072-
auto meetsProtocolBaseMetatypeCriteria =
4073-
baseTy->is<AnyMetatypeType>() && baseTy->getMetatypeInstanceType()
4074-
->getWithoutParens()
4075-
->isAnyExistentialType();
4076-
4077-
auto requiresProtocolMetatypeFix =
4078-
llvm::any_of(filteredCandidates, [&](const OverloadChoice &c) {
4079-
return c.isDecl() && isa<ConstructorDecl>(c.getDecl());
4080-
});
4081-
4082-
if (!meetsProtocolBaseMetatypeCriteria ||
4083-
!requiresProtocolMetatypeFix) {
4084-
auto fix =
4085-
AllowTypeOrInstanceMember::create(*this, baseTy, member, locator);
4086-
if (recordFix(fix))
4087-
// The fix wasn't successful, so return an error
4088-
return SolutionKind::Error;
4089-
}
4090-
4091-
addOverloadSet(memberTy, filteredCandidates, useDC, locator);
4242+
SmallVector<OverloadChoice, 8> choices;
4243+
llvm::transform(
4244+
result.UnviableCandidates, std::back_inserter(choices),
4245+
[](const std::pair<OverloadChoice, MemberLookupResult::UnviableReason>
4246+
&candidate) { return candidate.first; });
4247+
4248+
SmallVector<Constraint *, 4> candidates;
4249+
generateConstraints(candidates, memberTy, choices, useDC, locator,
4250+
/*favoredChoice=*/None, /*requiresFix=*/true,
4251+
[&](unsigned idx, const OverloadChoice &choice) {
4252+
return fixMemberRef(
4253+
*this, baseTy, member, choice, locator,
4254+
result.UnviableCandidates[idx].second);
4255+
});
4256+
4257+
// If there are any viable "fixed" candidates, let's schedule
4258+
// them to be attempted.
4259+
if (!candidates.empty()) {
4260+
addOverloadSet(candidates, locator);
40924261
return SolutionKind::Solved;
40934262
}
40944263
}
@@ -6379,6 +6548,11 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) {
63796548
None, constraint.getLocator());
63806549

63816550
case ConstraintKind::BindOverload:
6551+
if (auto *fix = constraint.getFix()) {
6552+
if (recordFix(fix))
6553+
return SolutionKind::Error;
6554+
}
6555+
63826556
resolveOverload(constraint.getLocator(), constraint.getFirstType(),
63836557
constraint.getOverloadChoice(),
63846558
constraint.getOverloadUseDC());

0 commit comments

Comments
 (0)