Merge remote-tracking branch 'origin/master' into master-rebranch

This commit is contained in:
swift-ci
2020-01-22 14:43:47 -08:00
7 changed files with 16 additions and 465 deletions

View File

@@ -220,150 +220,15 @@ private:
std::pair<Type, ContextualTypePurpose>
validateContextualType(Type contextualType, ContextualTypePurpose CTP);
/// Given a result of name lookup that had no viable results, diagnose the
/// unviable ones.
void diagnoseUnviableLookupResults(MemberLookupResult &lookupResults,
Expr *expr, Type baseObjTy, Expr *baseExpr,
DeclNameRef memberName,
DeclNameLoc nameLoc, SourceLoc loc);
bool diagnoseMemberFailures(
Expr *E, Expr *baseEpxr, ConstraintKind lookupKind,
DeclNameRef memberName, FunctionRefKind funcRefKind,
ConstraintLocator *locator,
Optional<std::function<bool(ArrayRef<OverloadChoice>)>> callback = None,
bool includeInaccessibleMembers = true);
bool visitExpr(Expr *E);
bool visitIdentityExpr(IdentityExpr *E);
bool visitTryExpr(TryExpr *E);
bool visitUnresolvedDotExpr(UnresolvedDotExpr *UDE);
bool visitApplyExpr(ApplyExpr *AE);
bool visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *E);
};
} // end anonymous namespace
/// Given a result of name lookup that had no viable results, diagnose the
/// unviable ones.
void FailureDiagnosis::diagnoseUnviableLookupResults(
MemberLookupResult &result, Expr *E, Type baseObjTy, Expr *baseExpr,
DeclNameRef memberName, DeclNameLoc nameLoc, SourceLoc loc) {
SourceRange baseRange = baseExpr ? baseExpr->getSourceRange() : SourceRange();
// If we found no results at all, mention that fact.
if (result.UnviableCandidates.empty()) {
MissingMemberFailure failure(CS, baseObjTy, memberName,
CS.getConstraintLocator(E));
auto diagnosed = failure.diagnoseAsError();
assert(diagnosed && "Failed to produce missing member diagnostic");
(void)diagnosed;
return;
}
// Otherwise, we have at least one (and potentially many) viable candidates
// sort them out. If all of the candidates have the same problem (commonly
// because there is exactly one candidate!) diagnose this.
auto firstProblem = result.UnviableReasons[0];
bool sameProblem = llvm::all_of(
result.UnviableReasons,
[&firstProblem](const MemberLookupResult::UnviableReason &problem) {
return problem == firstProblem;
});
auto instanceTy = baseObjTy;
if (auto *MTT = instanceTy->getAs<AnyMetatypeType>())
instanceTy = MTT->getInstanceType();
if (sameProblem) {
// If the problem is the same for all of the choices, let's
// just pick one which has a declaration.
auto choice = llvm::find_if(
result.UnviableCandidates,
[&](const OverloadChoice &choice) { return choice.isDecl(); });
// This code can't currently diagnose key path application
// related failures.
if (!choice)
return;
switch (firstProblem) {
case MemberLookupResult::UR_WritableKeyPathOnReadOnlyMember:
case MemberLookupResult::UR_ReferenceWritableKeyPathOnMutatingMember:
case MemberLookupResult::UR_KeyPathWithAnyObjectRootType:
break;
case MemberLookupResult::UR_UnavailableInExistential: {
InvalidMemberRefOnExistential failure(
CS, instanceTy, memberName, CS.getConstraintLocator(E));
failure.diagnoseAsError();
return;
}
case MemberLookupResult::UR_InstanceMemberOnType:
case MemberLookupResult::UR_TypeMemberOnInstance: {
auto locatorKind = isa<SubscriptExpr>(E)
? ConstraintLocator::SubscriptMember
: ConstraintLocator::Member;
AllowTypeOrInstanceMemberFailure failure(
CS, baseObjTy, choice->getDecl(), memberName,
CS.getConstraintLocator(E, locatorKind));
auto diagnosed = failure.diagnoseAsError();
assert(diagnosed &&
"Failed to produce missing or extraneous metatype diagnostic");
(void)diagnosed;
return;
}
case MemberLookupResult::UR_MutatingMemberOnRValue:
case MemberLookupResult::UR_MutatingGetterOnRValue: {
MutatingMemberRefOnImmutableBase failure(CS, choice->getDecl(),
CS.getConstraintLocator(E));
(void)failure.diagnose();
return;
}
case MemberLookupResult::UR_Inaccessible: {
// FIXME: What if the unviable candidates have different levels of access?
//
// If we found an inaccessible member of a protocol extension, it might
// be declared 'public'. This can only happen if the protocol is not
// visible to us, but the conforming type is. In this case, we need to
// clamp the formal access for diagnostics purposes to the formal access
// of the protocol itself.
InaccessibleMemberFailure failure(CS, choice->getDecl(),
CS.getConstraintLocator(E));
auto diagnosed = failure.diagnoseAsError();
assert(diagnosed && "failed to produce expected diagnostic");
for (auto cand : result.UnviableCandidates) {
if (!cand.isDecl())
continue;
auto *candidate = cand.getDecl();
// failure is going to highlight candidate given to it,
// we just need to handle the rest here.
if (candidate != choice->getDecl())
diagnose(candidate, diag::decl_declared_here,
candidate->getFullName());
}
return;
}
}
}
// Otherwise, we don't have a specific issue to diagnose. Just say the vague
// 'cannot use' diagnostic.
if (!baseObjTy->isEqual(instanceTy))
diagnose(loc, diag::could_not_use_type_member,
instanceTy, memberName)
.highlight(baseRange).highlight(nameLoc.getSourceRange());
else
diagnose(loc, diag::could_not_use_value_member,
baseObjTy, memberName)
.highlight(baseRange).highlight(nameLoc.getSourceRange());
return;
}
namespace {
class ExprTypeSaverAndEraser {
llvm::DenseMap<Expr*, Type> ExprTypes;
@@ -1538,10 +1403,6 @@ bool FailureDiagnosis::visitApplyExpr(ApplyExpr *callExpr) {
argLabels))
return true;
// Diagnose some simple and common errors.
if (calleeInfo.diagnoseSimpleErrors(callExpr))
return true;
// Force recheck of the arg expression because we allowed unresolved types
// before, and that turned out not to help, and now we want any diagnoses
// from disallowing them.
@@ -1721,244 +1582,6 @@ visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *E) {
return false;
}
bool FailureDiagnosis::diagnoseMemberFailures(
Expr *E, Expr *baseExpr, ConstraintKind lookupKind, DeclNameRef memberName,
FunctionRefKind funcRefKind, ConstraintLocator *locator,
Optional<std::function<bool(ArrayRef<OverloadChoice>)>> callback,
bool includeInaccessibleMembers) {
auto isInitializer = memberName.isSimpleName(DeclBaseName::createConstructor());
// Get the referenced base expression from the failed constraint, along with
// the SourceRange for the member ref. In "x.y", this returns the expr for x
// and the source range for y.
SourceRange memberRange;
SourceLoc BaseLoc;
DeclNameLoc NameLoc;
Type baseTy, baseObjTy;
// UnresolvedMemberExpr doesn't have "base" expression,
// it's represented as ".foo", which means that we need
// to get base from the context.
if (auto *UME = dyn_cast<UnresolvedMemberExpr>(E)) {
memberRange = E->getSourceRange();
BaseLoc = E->getLoc();
NameLoc = UME->getNameLoc();
baseTy = CS.getContextualType();
if (!baseTy)
return false;
// If we succeeded, get ready to do the member lookup.
baseObjTy = baseTy->getRValueType();
// If the base object is already a metatype type, then something weird is
// going on. For now, just generate a generic error.
if (baseObjTy->is<MetatypeType>())
return false;
baseTy = baseObjTy = MetatypeType::get(baseObjTy);
} else {
memberRange = baseExpr->getSourceRange();
if (locator)
locator = simplifyLocator(CS, locator, memberRange);
BaseLoc = baseExpr->getLoc();
NameLoc = DeclNameLoc(memberRange.Start);
// Retypecheck the anchor type, which is the base of the member expression.
baseExpr = typeCheckChildIndependently(baseExpr, TCC_AllowLValue);
if (!baseExpr)
return true;
baseTy = CS.getType(baseExpr);
baseObjTy = baseTy->getWithoutSpecifierType();
}
// If the base type is an IUO, look through it. Odds are, the code is not
// trying to find a member of it.
// FIXME: We need to rework this with IUOs out of the type system.
// if (auto objTy = CS.lookThroughImplicitlyUnwrappedOptionalType(baseObjTy))
// baseTy = baseObjTy = objTy;
// If the base of this property access is a function that takes an empty
// argument list, then the most likely problem is that the user wanted to
// call the function, e.g. in "a.b.c" where they had to write "a.b().c".
// Produce a specific diagnostic + fixit for this situation.
if (auto baseFTy = baseObjTy->getAs<AnyFunctionType>()) {
if (baseExpr && baseFTy->getParams().empty()) {
auto failure =
MissingCallFailure(CS, CS.getConstraintLocator(baseExpr));
return failure.diagnoseAsError();
}
}
// If this is a tuple, then the index needs to be valid.
if (auto tuple = baseObjTy->getAs<TupleType>()) {
auto baseName = memberName.getBaseName();
if (!baseName.isSpecial()) {
StringRef nameStr = baseName.userFacingName();
int fieldIdx = -1;
// Resolve a number reference into the tuple type.
unsigned Value = 0;
if (!nameStr.getAsInteger(10, Value) && Value < tuple->getNumElements()) {
fieldIdx = Value;
} else {
fieldIdx = tuple->getNamedElementId(memberName.getBaseIdentifier());
}
if (fieldIdx != -1)
return false; // Lookup is valid.
}
diagnose(BaseLoc, diag::could_not_find_tuple_member, baseObjTy, memberName)
.highlight(memberRange);
return true;
}
// If this is initializer/constructor lookup we are dealing this.
if (isInitializer) {
// Let's check what is the base type we are trying to look it up on
// because only MetatypeType is viable to find constructor on, as per
// rules in ConstraintSystem::performMemberLookup.
if (!baseTy->is<AnyMetatypeType>()) {
baseTy = MetatypeType::get(baseTy, CS.getASTContext());
}
}
// If base type has unresolved generic parameters, such might mean
// that it's initializer with erroneous argument, otherwise this would
// be a simple ambiguous archetype case, neither can be diagnosed here.
if (baseTy->hasTypeParameter() && baseTy->hasUnresolvedType())
return false;
MemberLookupResult result =
CS.performMemberLookup(lookupKind, memberName, baseTy, funcRefKind,
locator, includeInaccessibleMembers);
switch (result.OverallResult) {
case MemberLookupResult::Unsolved:
// If we couldn't resolve a specific type for the base expression, then we
// cannot produce a specific diagnostic.
return false;
case MemberLookupResult::ErrorAlreadyDiagnosed:
// If an error was already emitted, then we're done, don't emit anything
// redundant.
return true;
case MemberLookupResult::HasResults:
break;
}
SmallVector<OverloadChoice, 4> viableCandidatesToReport;
for (auto candidate : result.ViableCandidates)
if (candidate.getKind() != OverloadChoiceKind::KeyPathApplication)
viableCandidatesToReport.push_back(candidate);
// Since the lookup was allowing inaccessible members, let's check
// if it found anything of that sort, which is easy to diagnose.
bool allUnavailable =
!CS.getASTContext().LangOpts.DisableAvailabilityChecking;
bool allInaccessible = true;
for (auto &member : viableCandidatesToReport) {
if (!member.isDecl()) {
// if there is no declaration, this choice is implicitly available.
allUnavailable = false;
continue;
}
auto decl = member.getDecl();
// Check availability of the found choice.
if (!decl->getAttrs().isUnavailable(CS.getASTContext()))
allUnavailable = false;
if (decl->isAccessibleFrom(CS.DC))
allInaccessible = false;
}
// diagnoseSimpleErrors() should have diagnosed this scenario.
assert(!allInaccessible || viableCandidatesToReport.empty());
if (result.UnviableCandidates.empty() && isInitializer &&
!baseObjTy->is<AnyMetatypeType>()) {
if (auto ctorRef = dyn_cast<UnresolvedDotExpr>(E)) {
// Diagnose 'super.init', which can only appear inside another
// initializer, specially.
if (isa<SuperRefExpr>(ctorRef->getBase())) {
diagnose(BaseLoc, diag::super_initializer_not_in_initializer);
return true;
}
// Suggest inserting a call to 'type(of:)' to construct another object
// of the same dynamic type.
SourceRange fixItRng = ctorRef->getNameLoc().getSourceRange();
// Surround the caller in `type(of:)`.
diagnose(BaseLoc, diag::init_not_instance_member)
.fixItInsert(fixItRng.Start, "type(of: ")
.fixItInsertAfter(fixItRng.End, ")");
return true;
}
}
if (viableCandidatesToReport.empty()) {
// If this was an optional type let's check if the base type
// has requested member, if so - generate nice error saying that
// optional was not unwrapped, otherwise say that type value has
// no such member.
if (auto *OT = dyn_cast<OptionalType>(baseObjTy.getPointer())) {
auto optionalResult = CS.performMemberLookup(
lookupKind, memberName, OT->getBaseType(), funcRefKind, locator,
/*includeInaccessibleMembers*/ false);
switch (optionalResult.OverallResult) {
case MemberLookupResult::ErrorAlreadyDiagnosed:
// If an error was already emitted, then we're done, don't emit anything
// redundant.
return true;
case MemberLookupResult::Unsolved:
case MemberLookupResult::HasResults:
break;
}
if (!optionalResult.ViableCandidates.empty()) {
MemberAccessOnOptionalBaseFailure failure(
CS, CS.getConstraintLocator(baseExpr), memberName,
/*resultOptional=*/false);
return failure.diagnoseAsError();
}
}
// FIXME: Dig out the property DeclNameLoc.
diagnoseUnviableLookupResults(result, E, baseObjTy, baseExpr, memberName,
NameLoc, BaseLoc);
return true;
}
if (allUnavailable) {
auto firstDecl = viableCandidatesToReport[0].getDecl();
// FIXME: We need the enclosing CallExpr to rewrite the argument labels.
if (diagnoseExplicitUnavailability(firstDecl, BaseLoc, CS.DC,
/*call*/ nullptr))
return true;
}
return callback.hasValue() ? (*callback)(viableCandidatesToReport) : false;
}
bool FailureDiagnosis::visitUnresolvedDotExpr(UnresolvedDotExpr *UDE) {
auto *baseExpr = UDE->getBase();
auto *locator = CS.getConstraintLocator(UDE, ConstraintLocator::Member);
if (!locator)
return false;
return diagnoseMemberFailures(UDE, baseExpr, ConstraintKind::ValueMember,
UDE->getName(), UDE->getFunctionRefKind(),
locator);
}
/// An IdentityExpr doesn't change its argument, but it *can* propagate its
/// contextual type information down.
bool FailureDiagnosis::visitIdentityExpr(IdentityExpr *E) {

View File

@@ -5461,22 +5461,9 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
auto addChoice = [&](OverloadChoice candidate) {
auto decl = candidate.getDecl();
// In a pattern binding initializer, immediately reject all of its bound
// variables. These would otherwise allow circular references.
if (auto *PBI = dyn_cast<PatternBindingInitializer>(DC)) {
if (auto *VD = dyn_cast<VarDecl>(decl)) {
if (PBI->getBinding() == VD->getParentPatternBinding()) {
// If this is a recursive reference to an instance variable,
// try to see if we can give a good diagnostic by adding it as
// an unviable candidate.
if (!VD->isStatic()) {
result.addUnviable(candidate,
MemberLookupResult::UR_InstanceMemberOnType);
}
return;
}
}
}
// Reject circular references immediately.
if (decl->isRecursiveValidation())
return;
// If the result is invalid, skip it.
if (decl->isInvalid()) {
@@ -6040,10 +6027,6 @@ fixMemberRef(ConstraintSystem &cs, Type baseTy,
switch (*reason) {
case MemberLookupResult::UR_InstanceMemberOnType:
case MemberLookupResult::UR_TypeMemberOnInstance: {
if (choice.getKind() == OverloadChoiceKind::DynamicMemberLookup ||
choice.getKind() == OverloadChoiceKind::KeyPathDynamicMemberLookup)
return nullptr;
return choice.isDecl()
? AllowTypeOrInstanceMember::create(
cs, baseTy, choice.getDecl(), memberName, locator)
@@ -6372,14 +6355,6 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
}
}
// FIXME(diagnostics): Errors related to `AnyObject` could be diagnosed
// better in the future, relevant failure information has to be extracted
// from `performMemberLookup` result, in order to figure out if it was a
// simple labeling or # of arguments mismatch, or member with requested name
// really doesn't exist.
if (baseTy->isAnyObject())
return SolutionKind::Error;
result = performMemberLookup(kind, member, baseTy, functionRefKind, locator,
/*includeInaccessibleMembers*/ true);

View File

@@ -945,41 +945,3 @@ suggestPotentialOverloads(SourceLoc loc, bool isResult) {
suggestionText);
}
}
/// Emit a diagnostic and return true if this is an error condition we can
/// handle uniformly. This should be called after filtering the candidate
/// list.
bool CalleeCandidateInfo::diagnoseSimpleErrors(const Expr *E) {
SourceLoc loc = E->getLoc();
// Handle symbols marked as explicitly unavailable.
if (closeness == CC_Unavailable) {
auto decl = candidates[0].getDecl();
assert(decl && "Only decl-based candidates may be marked unavailable");
return diagnoseExplicitUnavailability(decl, loc, CS.DC,
dyn_cast<CallExpr>(E));
}
// Handle symbols that are matches, but are not accessible from the current
// scope.
if (closeness == CC_Inaccessible) {
auto decl = candidates[0].getDecl();
assert(decl && "Only decl-based candidates may be marked inaccessible");
InaccessibleMemberFailure failure(
CS, decl, CS.getConstraintLocator(const_cast<Expr *>(E)));
auto diagnosed = failure.diagnoseAsError();
assert(diagnosed && "failed to produce expected diagnostic");
for (auto cand : candidates) {
auto *candidate = cand.getDecl();
if (candidate && candidate != decl)
candidate->diagnose(diag::decl_declared_here, candidate->getFullName());
}
return true;
}
return false;
}

View File

@@ -227,11 +227,6 @@ namespace swift {
/// overloads.
void suggestPotentialOverloads(SourceLoc loc, bool isResult = false);
/// Emit a diagnostic and return true if this is an error condition we can
/// handle uniformly. This should be called after filtering the candidate
/// list.
bool diagnoseSimpleErrors(const Expr *E);
void dump(llvm::raw_ostream &os) const;
SWIFT_DEBUG_DUMP;