Skip to content

Commit f1f6e0f

Browse files
committedJun 7, 2019
[CodeComplete] Improve overload handling for C++ qualified and ref-qualified methods.
Summary: - when a method is not available because of the target value kind (e.g. an && method on a Foo& variable), then don't offer it. - when a method is effectively shadowed by another method from the same class with a) an identical argument list and b) superior qualifiers, then don't offer it. Reviewers: ilya-biryukov Subscribers: cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D62582 llvm-svn: 362785
1 parent 15fec3a commit f1f6e0f

File tree

2 files changed

+174
-14
lines changed

2 files changed

+174
-14
lines changed
 

‎clang/lib/Sema/SemaCodeComplete.cpp

+111-14
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
#include "clang/AST/ExprCXX.h"
1717
#include "clang/AST/ExprObjC.h"
1818
#include "clang/AST/QualTypeNames.h"
19+
#include "clang/AST/Type.h"
1920
#include "clang/Basic/CharInfo.h"
21+
#include "clang/Basic/Specifiers.h"
2022
#include "clang/Lex/HeaderSearch.h"
2123
#include "clang/Lex/MacroInfo.h"
2224
#include "clang/Lex/Preprocessor.h"
@@ -152,9 +154,16 @@ class ResultBuilder {
152154
/// different levels of, e.g., the inheritance hierarchy.
153155
std::list<ShadowMap> ShadowMaps;
154156

157+
/// Overloaded C++ member functions found by SemaLookup.
158+
/// Used to determine when one overload is dominated by another.
159+
llvm::DenseMap<std::pair<DeclContext *, /*Name*/uintptr_t>, ShadowMapEntry>
160+
OverloadMap;
161+
155162
/// If we're potentially referring to a C++ member function, the set
156163
/// of qualifiers applied to the object type.
157164
Qualifiers ObjectTypeQualifiers;
165+
/// The kind of the object expression, for rvalue/lvalue overloads.
166+
ExprValueKind ObjectKind;
158167

159168
/// Whether the \p ObjectTypeQualifiers field is active.
160169
bool HasObjectTypeQualifiers;
@@ -230,8 +239,9 @@ class ResultBuilder {
230239
/// out member functions that aren't available (because there will be a
231240
/// cv-qualifier mismatch) or prefer functions with an exact qualifier
232241
/// match.
233-
void setObjectTypeQualifiers(Qualifiers Quals) {
242+
void setObjectTypeQualifiers(Qualifiers Quals, ExprValueKind Kind) {
234243
ObjectTypeQualifiers = Quals;
244+
ObjectKind = Kind;
235245
HasObjectTypeQualifiers = true;
236246
}
237247

@@ -1157,6 +1167,53 @@ static void setInBaseClass(ResultBuilder::Result &R) {
11571167
R.InBaseClass = true;
11581168
}
11591169

1170+
enum class OverloadCompare { BothViable, Dominates, Dominated };
1171+
// Will Candidate ever be called on the object, when overloaded with Incumbent?
1172+
// Returns Dominates if Candidate is always called, Dominated if Incumbent is
1173+
// always called, BothViable if either may be called dependending on arguments.
1174+
// Precondition: must actually be overloads!
1175+
static OverloadCompare compareOverloads(const CXXMethodDecl &Candidate,
1176+
const CXXMethodDecl &Incumbent,
1177+
const Qualifiers &ObjectQuals,
1178+
ExprValueKind ObjectKind) {
1179+
if (Candidate.isVariadic() != Incumbent.isVariadic() ||
1180+
Candidate.getNumParams() != Incumbent.getNumParams() ||
1181+
Candidate.getMinRequiredArguments() !=
1182+
Incumbent.getMinRequiredArguments())
1183+
return OverloadCompare::BothViable;
1184+
for (unsigned I = 0, E = Candidate.getNumParams(); I != E; ++I)
1185+
if (Candidate.parameters()[I]->getType().getCanonicalType() !=
1186+
Incumbent.parameters()[I]->getType().getCanonicalType())
1187+
return OverloadCompare::BothViable;
1188+
if (!llvm::empty(Candidate.specific_attrs<EnableIfAttr>()) ||
1189+
!llvm::empty(Incumbent.specific_attrs<EnableIfAttr>()))
1190+
return OverloadCompare::BothViable;
1191+
// At this point, we know calls can't pick one or the other based on
1192+
// arguments, so one of the two must win. (Or both fail, handled elsewhere).
1193+
RefQualifierKind CandidateRef = Candidate.getRefQualifier();
1194+
RefQualifierKind IncumbentRef = Incumbent.getRefQualifier();
1195+
if (CandidateRef != IncumbentRef) {
1196+
// If the object kind is LValue/RValue, there's one acceptable ref-qualifier
1197+
// and it can't be mixed with ref-unqualified overloads (in valid code).
1198+
1199+
// For xvalue objects, we prefer the rvalue overload even if we have to
1200+
// add qualifiers (which is rare, because const&& is rare).
1201+
if (ObjectKind == clang::VK_XValue)
1202+
return CandidateRef == RQ_RValue ? OverloadCompare::Dominates
1203+
: OverloadCompare::Dominated;
1204+
}
1205+
// Now the ref qualifiers are the same (or we're in some invalid state).
1206+
// So make some decision based on the qualifiers.
1207+
Qualifiers CandidateQual = Candidate.getMethodQualifiers();
1208+
Qualifiers IncumbentQual = Incumbent.getMethodQualifiers();
1209+
bool CandidateSuperset = CandidateQual.compatiblyIncludes(IncumbentQual);
1210+
bool IncumbentSuperset = IncumbentQual.compatiblyIncludes(CandidateQual);
1211+
if (CandidateSuperset == IncumbentSuperset)
1212+
return OverloadCompare::BothViable;
1213+
return IncumbentSuperset ? OverloadCompare::Dominates
1214+
: OverloadCompare::Dominated;
1215+
}
1216+
11601217
void ResultBuilder::AddResult(Result R, DeclContext *CurContext,
11611218
NamedDecl *Hiding, bool InBaseClass = false) {
11621219
if (R.Kind != Result::RK_Declaration) {
@@ -1233,6 +1290,44 @@ void ResultBuilder::AddResult(Result R, DeclContext *CurContext,
12331290
// qualifiers.
12341291
return;
12351292
}
1293+
// Detect cases where a ref-qualified method cannot be invoked.
1294+
switch (Method->getRefQualifier()) {
1295+
case RQ_LValue:
1296+
if (ObjectKind != VK_LValue && !MethodQuals.hasConst())
1297+
return;
1298+
break;
1299+
case RQ_RValue:
1300+
if (ObjectKind == VK_LValue)
1301+
return;
1302+
break;
1303+
case RQ_None:
1304+
break;
1305+
}
1306+
1307+
/// Check whether this dominates another overloaded method, which should
1308+
/// be suppressed (or vice versa).
1309+
/// Motivating case is const_iterator begin() const vs iterator begin().
1310+
auto &OverloadSet = OverloadMap[std::make_pair(
1311+
CurContext, Method->getDeclName().getAsOpaqueInteger())];
1312+
for (const DeclIndexPair& Entry : OverloadSet) {
1313+
Result &Incumbent = Results[Entry.second];
1314+
switch (compareOverloads(*Method,
1315+
*cast<CXXMethodDecl>(Incumbent.Declaration),
1316+
ObjectTypeQualifiers, ObjectKind)) {
1317+
case OverloadCompare::Dominates:
1318+
// Replace the dominated overload with this one.
1319+
// FIXME: if the overload dominates multiple incumbents then we
1320+
// should remove all. But two overloads is by far the common case.
1321+
Incumbent = std::move(R);
1322+
return;
1323+
case OverloadCompare::Dominated:
1324+
// This overload can't be called, drop it.
1325+
return;
1326+
case OverloadCompare::BothViable:
1327+
break;
1328+
}
1329+
}
1330+
OverloadSet.Add(Method, Results.size());
12361331
}
12371332

12381333
// Insert this result into the set of results.
@@ -3997,7 +4092,8 @@ void Sema::CodeCompleteOrdinaryName(Scope *S,
39974092
// the member function to filter/prioritize the results list.
39984093
auto ThisType = getCurrentThisType();
39994094
if (!ThisType.isNull())
4000-
Results.setObjectTypeQualifiers(ThisType->getPointeeType().getQualifiers());
4095+
Results.setObjectTypeQualifiers(ThisType->getPointeeType().getQualifiers(),
4096+
VK_LValue);
40014097

40024098
CodeCompletionDeclConsumer Consumer(Results, CurContext);
40034099
LookupVisibleDecls(S, LookupOrdinaryName, Consumer,
@@ -4551,13 +4647,12 @@ AddObjCProperties(const CodeCompletionContext &CCContext,
45514647
}
45524648
}
45534649

4554-
static void
4555-
AddRecordMembersCompletionResults(Sema &SemaRef, ResultBuilder &Results,
4556-
Scope *S, QualType BaseType, RecordDecl *RD,
4557-
Optional<FixItHint> AccessOpFixIt) {
4650+
static void AddRecordMembersCompletionResults(
4651+
Sema &SemaRef, ResultBuilder &Results, Scope *S, QualType BaseType,
4652+
ExprValueKind BaseKind, RecordDecl *RD, Optional<FixItHint> AccessOpFixIt) {
45584653
// Indicate that we are performing a member access, and the cv-qualifiers
45594654
// for the base object type.
4560-
Results.setObjectTypeQualifiers(BaseType.getQualifiers());
4655+
Results.setObjectTypeQualifiers(BaseType.getQualifiers(), BaseKind);
45614656

45624657
// Access to a C/C++ class, struct, or union.
45634658
Results.allowNestedNameSpecifiers();
@@ -4638,18 +4733,20 @@ void Sema::CodeCompleteMemberReferenceExpr(Scope *S, Expr *Base,
46384733
Base = ConvertedBase.get();
46394734

46404735
QualType BaseType = Base->getType();
4736+
ExprValueKind BaseKind = Base->getValueKind();
46414737

46424738
if (IsArrow) {
4643-
if (const PointerType *Ptr = BaseType->getAs<PointerType>())
4739+
if (const PointerType *Ptr = BaseType->getAs<PointerType>()) {
46444740
BaseType = Ptr->getPointeeType();
4645-
else if (BaseType->isObjCObjectPointerType())
4741+
BaseKind = VK_LValue;
4742+
} else if (BaseType->isObjCObjectPointerType())
46464743
/*Do nothing*/;
46474744
else
46484745
return false;
46494746
}
46504747

46514748
if (const RecordType *Record = BaseType->getAs<RecordType>()) {
4652-
AddRecordMembersCompletionResults(*this, Results, S, BaseType,
4749+
AddRecordMembersCompletionResults(*this, Results, S, BaseType, BaseKind,
46534750
Record->getDecl(),
46544751
std::move(AccessOpFixIt));
46554752
} else if (const auto *TST =
@@ -4658,13 +4755,13 @@ void Sema::CodeCompleteMemberReferenceExpr(Scope *S, Expr *Base,
46584755
if (const auto *TD =
46594756
dyn_cast_or_null<ClassTemplateDecl>(TN.getAsTemplateDecl())) {
46604757
CXXRecordDecl *RD = TD->getTemplatedDecl();
4661-
AddRecordMembersCompletionResults(*this, Results, S, BaseType, RD,
4662-
std::move(AccessOpFixIt));
4758+
AddRecordMembersCompletionResults(*this, Results, S, BaseType, BaseKind,
4759+
RD, std::move(AccessOpFixIt));
46634760
}
46644761
} else if (const auto *ICNT = BaseType->getAs<InjectedClassNameType>()) {
46654762
if (auto *RD = ICNT->getDecl())
4666-
AddRecordMembersCompletionResults(*this, Results, S, BaseType, RD,
4667-
std::move(AccessOpFixIt));
4763+
AddRecordMembersCompletionResults(*this, Results, S, BaseType, BaseKind,
4764+
RD, std::move(AccessOpFixIt));
46684765
} else if (!IsArrow && BaseType->isObjCObjectPointerType()) {
46694766
// Objective-C property reference.
46704767
AddedPropertiesSet AddedProperties;

‎clang/test/CodeCompletion/member-access.cpp

+63
Original file line numberDiff line numberDiff line change
@@ -210,3 +210,66 @@ void test3(const Proxy2 &p) {
210210
// CHECK-CC9: memfun2 (InBase) : [#void#][#Base3::#]memfun2(<#int#>) (requires fix-it: {181:4-181:5} to "->")
211211
// CHECK-CC9: memfun3 : [#int#]memfun3(<#int#>) (requires fix-it: {181:4-181:5} to "->")
212212
// CHECK-CC9: operator-> : [#Derived *#]operator->()[# const#]
213+
214+
// These overload sets differ only by return type and this-qualifiers.
215+
// So for any given callsite, only one is available.
216+
struct Overloads {
217+
double ConstOverload(char);
218+
int ConstOverload(char) const;
219+
220+
int RefOverload(char) &;
221+
double RefOverload(char) const&;
222+
char RefOverload(char) &&;
223+
};
224+
void testLValue(Overloads& Ref) {
225+
Ref.
226+
}
227+
void testConstLValue(const Overloads& ConstRef) {
228+
ConstRef.
229+
}
230+
void testRValue() {
231+
Overloads().
232+
}
233+
void testXValue(Overloads& X) {
234+
static_cast<Overloads&&>(X).
235+
}
236+
237+
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:225:7 %s -o - | FileCheck -check-prefix=CHECK-LVALUE %s \
238+
// RUN: --implicit-check-not="[#int#]ConstOverload(" \
239+
// RUN: --implicit-check-not="[#double#]RefOverload(" \
240+
// RUN: --implicit-check-not="[#char#]RefOverload("
241+
// CHECK-LVALUE-DAG: [#double#]ConstOverload(
242+
// CHECK-LVALUE-DAG: [#int#]RefOverload(
243+
244+
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:228:12 %s -o - | FileCheck -check-prefix=CHECK-CONSTLVALUE %s \
245+
// RUN: --implicit-check-not="[#double#]ConstOverload(" \
246+
// RUN: --implicit-check-not="[#int#]RefOverload(" \
247+
// RUN: --implicit-check-not="[#char#]RefOverload("
248+
// CHECK-CONSTLVALUE: [#int#]ConstOverload(
249+
// CHECK-CONSTLVALUE: [#double#]RefOverload(
250+
251+
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:231:15 %s -o - | FileCheck -check-prefix=CHECK-PRVALUE %s \
252+
// RUN: --implicit-check-not="[#int#]ConstOverload(" \
253+
// RUN: --implicit-check-not="[#int#]RefOverload(" \
254+
// RUN: --implicit-check-not="[#double#]RefOverload("
255+
// CHECK-PRVALUE: [#double#]ConstOverload(
256+
// CHECK-PRVALUE: [#char#]RefOverload(
257+
258+
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:234:31 %s -o - | FileCheck -check-prefix=CHECK-XVALUE %s \
259+
// RUN: --implicit-check-not="[#int#]ConstOverload(" \
260+
// RUN: --implicit-check-not="[#int#]RefOverload(" \
261+
// RUN: --implicit-check-not="[#double#]RefOverload("
262+
// CHECK-XVALUE: [#double#]ConstOverload(
263+
// CHECK-XVALUE: [#char#]RefOverload(
264+
265+
void testOverloadOperator() {
266+
struct S {
267+
char operator=(int) const;
268+
int operator=(int);
269+
} s;
270+
return s.
271+
}
272+
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:270:12 %s -o - | FileCheck -check-prefix=CHECK-OPER %s \
273+
// RUN: --implicit-check-not="[#char#]operator=("
274+
// CHECK-OPER: [#int#]operator=(
275+

0 commit comments

Comments
 (0)
Please sign in to comment.