Skip to content

Commit 7a6f493

Browse files
authored
Merge pull request #25138 from sl/port-member-access-on-existential-diagnostic
Sema: Port member access on existentials diagnostic to new diagnostic framework
2 parents d44edf4 + dc886eb commit 7a6f493

File tree

7 files changed

+137
-25
lines changed

7 files changed

+137
-25
lines changed

lib/Sema/CSDiag.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -755,11 +755,14 @@ void FailureDiagnosis::diagnoseUnviableLookupResults(
755755
case MemberLookupResult::UR_ReferenceWritableKeyPathOnMutatingMember:
756756
case MemberLookupResult::UR_KeyPathWithAnyObjectRootType:
757757
break;
758-
case MemberLookupResult::UR_UnavailableInExistential:
759-
diagnose(loc, diag::could_not_use_member_on_existential,
760-
instanceTy, memberName)
761-
.highlight(baseRange).highlight(nameLoc.getSourceRange());
758+
759+
case MemberLookupResult::UR_UnavailableInExistential: {
760+
InvalidMemberRefOnExistential failure(
761+
baseExpr, CS, instanceTy, memberName, CS.getConstraintLocator(E));
762+
failure.diagnoseAsError();
762763
return;
764+
}
765+
763766
case MemberLookupResult::UR_InstanceMemberOnType:
764767
case MemberLookupResult::UR_TypeMemberOnInstance: {
765768
auto locatorKind = isa<SubscriptExpr>(E)

lib/Sema/CSDiagnostics.cpp

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1844,7 +1844,7 @@ bool MissingMemberFailure::diagnoseAsError() {
18441844
if (!anchor || !baseExpr)
18451845
return false;
18461846

1847-
if (auto *typeVar = BaseType->getAs<TypeVariableType>()) {
1847+
if (auto *typeVar = getBaseType()->getAs<TypeVariableType>()) {
18481848
auto &CS = getConstraintSystem();
18491849
auto *memberLoc = typeVar->getImpl().getLocator();
18501850
// Don't try to diagnose anything besides first missing
@@ -1856,7 +1856,7 @@ bool MissingMemberFailure::diagnoseAsError() {
18561856
return false;
18571857
}
18581858

1859-
auto baseType = resolveType(BaseType)->getWithoutSpecifierType();
1859+
auto baseType = resolveType(getBaseType())->getWithoutSpecifierType();
18601860

18611861
DeclNameLoc nameLoc(anchor->getStartLoc());
18621862
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(anchor)) {
@@ -1876,22 +1876,22 @@ bool MissingMemberFailure::diagnoseAsError() {
18761876
if (baseType->is<TupleType>())
18771877
diagnostic = diag::could_not_find_tuple_member;
18781878

1879-
emitDiagnostic(anchor->getLoc(), diagnostic, baseType, Name)
1879+
emitDiagnostic(anchor->getLoc(), diagnostic, baseType, getName())
18801880
.highlight(baseExpr->getSourceRange())
18811881
.highlight(nameLoc.getSourceRange());
18821882
};
18831883

1884-
TypoCorrectionResults corrections(TC, Name, nameLoc);
1884+
TypoCorrectionResults corrections(TC, getName(), nameLoc);
18851885
auto tryTypoCorrection = [&] {
18861886
TC.performTypoCorrection(getDC(), DeclRefKind::Ordinary, baseType,
18871887
defaultMemberLookupOptions, corrections);
18881888
};
18891889

1890-
if (Name.getBaseName().getKind() == DeclBaseName::Kind::Subscript) {
1890+
if (getName().getBaseName().getKind() == DeclBaseName::Kind::Subscript) {
18911891
emitDiagnostic(anchor->getLoc(), diag::could_not_find_value_subscript,
18921892
baseType)
18931893
.highlight(baseExpr->getSourceRange());
1894-
} else if (Name.getBaseName() == "deinit") {
1894+
} else if (getName().getBaseName() == "deinit") {
18951895
// Specialised diagnostic if trying to access deinitialisers
18961896
emitDiagnostic(anchor->getLoc(), diag::destructor_not_accessible)
18971897
.highlight(baseExpr->getSourceRange());
@@ -1900,9 +1900,9 @@ bool MissingMemberFailure::diagnoseAsError() {
19001900
tryTypoCorrection();
19011901

19021902
if (DeclName rightName =
1903-
findCorrectEnumCaseName(instanceTy, corrections, Name)) {
1903+
findCorrectEnumCaseName(instanceTy, corrections, getName())) {
19041904
emitDiagnostic(anchor->getLoc(), diag::could_not_find_enum_case,
1905-
instanceTy, Name, rightName)
1905+
instanceTy, getName(), rightName)
19061906
.fixItReplace(nameLoc.getBaseNameLoc(),
19071907
rightName.getBaseIdentifier().str());
19081908
return true;
@@ -1911,7 +1911,7 @@ bool MissingMemberFailure::diagnoseAsError() {
19111911
if (auto correction = corrections.claimUniqueCorrection()) {
19121912
auto diagnostic = emitDiagnostic(
19131913
anchor->getLoc(), diag::could_not_find_type_member_corrected,
1914-
instanceTy, Name, correction->CorrectedName);
1914+
instanceTy, getName(), correction->CorrectedName);
19151915
diagnostic.highlight(baseExpr->getSourceRange())
19161916
.highlight(nameLoc.getSourceRange());
19171917
correction->addFixits(diagnostic);
@@ -1920,14 +1920,14 @@ bool MissingMemberFailure::diagnoseAsError() {
19201920
}
19211921
} else if (auto moduleTy = baseType->getAs<ModuleType>()) {
19221922
emitDiagnostic(baseExpr->getLoc(), diag::no_member_of_module,
1923-
moduleTy->getModule()->getName(), Name)
1923+
moduleTy->getModule()->getName(), getName())
19241924
.highlight(baseExpr->getSourceRange())
19251925
.highlight(nameLoc.getSourceRange());
19261926
return true;
19271927
} else {
19281928
// Check for a few common cases that can cause missing members.
19291929
auto *ED = baseType->getEnumOrBoundGenericEnum();
1930-
if (ED && Name.isSimpleName("rawValue")) {
1930+
if (ED && getName().isSimpleName("rawValue")) {
19311931
auto loc = ED->getNameLoc();
19321932
if (loc.isValid()) {
19331933
emitBasicError(baseType);
@@ -1947,7 +1947,7 @@ bool MissingMemberFailure::diagnoseAsError() {
19471947
if (auto correction = corrections.claimUniqueCorrection()) {
19481948
auto diagnostic = emitDiagnostic(
19491949
anchor->getLoc(), diag::could_not_find_value_member_corrected,
1950-
baseType, Name, correction->CorrectedName);
1950+
baseType, getName(), correction->CorrectedName);
19511951
diagnostic.highlight(baseExpr->getSourceRange())
19521952
.highlight(nameLoc.getSourceRange());
19531953
correction->addFixits(diagnostic);
@@ -1961,6 +1961,30 @@ bool MissingMemberFailure::diagnoseAsError() {
19611961
return true;
19621962
}
19631963

1964+
bool InvalidMemberRefOnExistential::diagnoseAsError() {
1965+
auto *anchor = getRawAnchor();
1966+
1967+
Expr *baseExpr = getAnchor();
1968+
DeclNameLoc nameLoc;
1969+
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(anchor)) {
1970+
baseExpr = UDE->getBase();
1971+
nameLoc = UDE->getNameLoc();
1972+
} else if (auto *UME = dyn_cast<UnresolvedMemberExpr>(anchor)) {
1973+
nameLoc = UME->getNameLoc();
1974+
} else if (auto *SE = dyn_cast<SubscriptExpr>(anchor)) {
1975+
baseExpr = SE->getBase();
1976+
} else if (auto *call = dyn_cast<CallExpr>(anchor)) {
1977+
baseExpr = call->getFn();
1978+
}
1979+
1980+
emitDiagnostic(getAnchor()->getLoc(),
1981+
diag::could_not_use_member_on_existential, getBaseType(),
1982+
getName())
1983+
.highlight(nameLoc.getSourceRange())
1984+
.highlight(baseExpr->getSourceRange());
1985+
return true;
1986+
}
1987+
19641988
bool AllowTypeOrInstanceMemberFailure::diagnoseAsError() {
19651989
auto loc = getAnchor()->getLoc();
19661990
auto &cs = getConstraintSystem();

lib/Sema/CSDiagnostics.h

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,21 @@ class SubscriptMisuseFailure final : public FailureDiagnostic {
762762
bool diagnoseAsNote() override;
763763
};
764764

765+
class InvalidMemberRefFailure : public FailureDiagnostic {
766+
Type BaseType;
767+
DeclName Name;
768+
769+
public:
770+
InvalidMemberRefFailure(Expr *root, ConstraintSystem &cs, Type baseType,
771+
DeclName memberName, ConstraintLocator *locator)
772+
: FailureDiagnostic(root, cs, locator), BaseType(baseType->getRValueType()),
773+
Name(memberName) {}
774+
775+
protected:
776+
Type getBaseType() const { return BaseType; }
777+
DeclName getName() const { return Name; }
778+
};
779+
765780
/// Diagnose situations when member referenced by name is missing
766781
/// from the associated base type, e.g.
767782
///
@@ -771,15 +786,11 @@ class SubscriptMisuseFailure final : public FailureDiagnostic {
771786
/// let _: Int = s.foo(1, 2) // expected type is `(Int, Int) -> Int`
772787
/// }
773788
/// ```
774-
class MissingMemberFailure final : public FailureDiagnostic {
775-
Type BaseType;
776-
DeclName Name;
777-
789+
class MissingMemberFailure final : public InvalidMemberRefFailure {
778790
public:
779791
MissingMemberFailure(Expr *root, ConstraintSystem &cs, Type baseType,
780792
DeclName memberName, ConstraintLocator *locator)
781-
: FailureDiagnostic(root, cs, locator), BaseType(baseType),
782-
Name(memberName) {}
793+
: InvalidMemberRefFailure(root, cs, baseType, memberName, locator) {}
783794

784795
bool diagnoseAsError() override;
785796

@@ -789,6 +800,28 @@ class MissingMemberFailure final : public FailureDiagnostic {
789800
DeclName memberName);
790801
};
791802

803+
/// Diagnose cases where a member only accessible on generic constraints
804+
/// requiring conformance to a protocol is used on a value of the
805+
/// existential protocol type e.g.
806+
///
807+
/// ```swift
808+
/// protocol P {
809+
/// var foo: Self { get }
810+
/// }
811+
///
812+
/// func bar<X : P>(p: X) {
813+
/// p.foo
814+
/// }
815+
/// ```
816+
class InvalidMemberRefOnExistential final : public InvalidMemberRefFailure {
817+
public:
818+
InvalidMemberRefOnExistential(Expr *root, ConstraintSystem &cs, Type baseType,
819+
DeclName memberName, ConstraintLocator *locator)
820+
: InvalidMemberRefFailure(root, cs, baseType, memberName, locator) {}
821+
822+
bool diagnoseAsError() override;
823+
};
824+
792825
/// Diagnose situations when we use an instance member on a type
793826
/// or a type member on an instance
794827
///

lib/Sema/CSFix.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,20 @@ DefineMemberBasedOnUse::create(ConstraintSystem &cs, Type baseType,
283283
DefineMemberBasedOnUse(cs, baseType, member, locator);
284284
}
285285

286+
AllowMemberRefOnExistential *
287+
AllowMemberRefOnExistential::create(ConstraintSystem &cs, Type baseType,
288+
ValueDecl *member, DeclName memberName,
289+
ConstraintLocator *locator) {
290+
return new (cs.getAllocator())
291+
AllowMemberRefOnExistential(cs, baseType, memberName, member, locator);
292+
}
293+
294+
bool AllowMemberRefOnExistential::diagnose(Expr *root, bool asNote) const {
295+
auto failure = InvalidMemberRefOnExistential(root, getConstraintSystem(),
296+
BaseType, Name, getLocator());
297+
return failure.diagnose(asNote);
298+
}
299+
286300
bool AllowTypeOrInstanceMember::diagnose(Expr *root, bool asNote) const {
287301
auto failure = AllowTypeOrInstanceMemberFailure(
288302
root, getConstraintSystem(), BaseType, Member, UsedName, getLocator());

lib/Sema/CSFix.h

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ enum class FixKind : uint8_t {
125125
/// referenced constructor must be required.
126126
AllowInvalidInitRef,
127127

128+
/// Allow an invalid member access on a value of protocol type as if
129+
/// that protocol type were a generic constraint requiring conformance
130+
/// to that protocol.
131+
AllowMemberRefOnExistential,
132+
128133
/// If there are fewer arguments than parameters, let's fix that up
129134
/// by adding new arguments to the list represented as type variables.
130135
AddMissingArguments,
@@ -592,7 +597,33 @@ class DefineMemberBasedOnUse final : public ConstraintFix {
592597
DeclName member,
593598
ConstraintLocator *locator);
594599
};
595-
600+
601+
class AllowMemberRefOnExistential final : public ConstraintFix {
602+
Type BaseType;
603+
DeclName Name;
604+
605+
AllowMemberRefOnExistential(ConstraintSystem &cs, Type baseType,
606+
DeclName memberName, ValueDecl *member,
607+
ConstraintLocator *locator)
608+
: ConstraintFix(cs, FixKind::AllowMemberRefOnExistential, locator),
609+
BaseType(baseType), Name(memberName) {}
610+
611+
public:
612+
std::string getName() const override {
613+
llvm::SmallVector<char, 16> scratch;
614+
auto memberName = Name.getString(scratch);
615+
return "allow access to invalid member '" + memberName.str() +
616+
"' on value of protocol type";
617+
}
618+
619+
bool diagnose(Expr *root, bool asNote = false) const override;
620+
621+
static AllowMemberRefOnExistential *create(ConstraintSystem &cs,
622+
Type baseType, ValueDecl *member,
623+
DeclName memberName,
624+
ConstraintLocator *locator);
625+
};
626+
596627
class AllowTypeOrInstanceMember final : public ConstraintFix {
597628
Type BaseType;
598629
ValueDecl *Member;

lib/Sema/CSSimplify.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4529,10 +4529,16 @@ fixMemberRef(ConstraintSystem &cs, Type baseTy,
45294529
assert(choice.isDecl());
45304530
return AllowInaccessibleMember::create(cs, choice.getDecl(), locator);
45314531

4532+
case MemberLookupResult::UR_UnavailableInExistential: {
4533+
return choice.isDecl()
4534+
? AllowMemberRefOnExistential::create(
4535+
cs, baseTy, choice.getDecl(), memberName, locator)
4536+
: nullptr;
4537+
}
4538+
45324539
case MemberLookupResult::UR_MutatingMemberOnRValue:
45334540
case MemberLookupResult::UR_MutatingGetterOnRValue:
45344541
case MemberLookupResult::UR_LabelMismatch:
4535-
case MemberLookupResult::UR_UnavailableInExistential:
45364542
// TODO(diagnostics): Add a new fix that is suggests to
45374543
// add `subscript(dynamicMember: {Writable}KeyPath<T, U>)`
45384544
// overload here, that would help if such subscript has
@@ -6637,6 +6643,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
66376643
case FixKind::RelabelArguments:
66386644
case FixKind::RemoveUnwrap:
66396645
case FixKind::DefineMemberBasedOnUse:
6646+
case FixKind::AllowMemberRefOnExistential:
66406647
case FixKind::AllowTypeOrInstanceMember:
66416648
case FixKind::AllowInvalidPartialApplication:
66426649
case FixKind::AllowInvalidInitRef:

test/decl/protocol/protocols.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ struct WrongIsEqual : IsEqualComparable { // expected-error{{type 'WrongIsEqual'
267267
//===----------------------------------------------------------------------===//
268268

269269
func existentialSequence(_ e: Sequence) { // expected-error{{has Self or associated type requirements}}
270-
var x = e.makeIterator() // expected-error{{value of type 'Sequence' has no member 'makeIterator'}}
270+
var x = e.makeIterator() // expected-error{{member 'makeIterator' cannot be used on value of protocol type 'Sequence'; use a generic constraint instead}}
271271
x.next()
272272
x.nonexistent()
273273
}

0 commit comments

Comments
 (0)