Skip to content

Commit acf07dc

Browse files
authored
[Clang] Do not treat Foo -> const Foo conversion sequences as perfect (#148613)
For implicit object arguments. This fixes a regression introduced by the "perfect match" overload resolution mechanism introduced in 8c5a307. Note that GCC allows the ambiguity between a const and non-const candidate to be resolved. But this patch focuses on restoring the Clang 20 behavior, and to fix the cases which we did resolve incorrectly. Fixes #147374
1 parent dae72bc commit acf07dc

File tree

3 files changed

+49
-4
lines changed

3 files changed

+49
-4
lines changed

clang/include/clang/Sema/Overload.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,11 @@ class Sema;
350350
LLVM_PREFERRED_TYPE(bool)
351351
unsigned BindsToRvalue : 1;
352352

353+
/// Whether this was an identity conversion with qualification
354+
/// conversion for the implicit object argument.
355+
LLVM_PREFERRED_TYPE(bool)
356+
unsigned IsImplicitObjectArgumentQualificationConversion : 1;
357+
353358
/// Whether this binds an implicit object argument to a
354359
/// non-static member function without a ref-qualifier.
355360
LLVM_PREFERRED_TYPE(bool)
@@ -448,11 +453,11 @@ class Sema;
448453
#endif
449454
return true;
450455
}
451-
if (!C.hasSameType(getFromType(), getToType(2)))
452-
return false;
453456
if (BindsToRvalue && IsLvalueReference)
454457
return false;
455-
return true;
458+
if (IsImplicitObjectArgumentQualificationConversion)
459+
return C.hasSameUnqualifiedType(getFromType(), getToType(2));
460+
return C.hasSameType(getFromType(), getToType(2));
456461
}
457462

458463
ImplicitConversionRank getRank() const;

clang/lib/Sema/SemaOverload.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ void StandardConversionSequence::setAsIdentityConversion() {
245245
IsLvalueReference = true;
246246
BindsToFunctionLvalue = false;
247247
BindsToRvalue = false;
248+
IsImplicitObjectArgumentQualificationConversion = false;
248249
BindsImplicitObjectArgumentWithoutRefQualifier = false;
249250
ObjCLifetimeConversionBinding = false;
250251
FromBracedInitList = false;
@@ -5317,6 +5318,7 @@ TryReferenceInit(Sema &S, Expr *Init, QualType DeclType,
53175318
ICS.Standard.DirectBinding = BindsDirectly;
53185319
ICS.Standard.IsLvalueReference = !isRValRef;
53195320
ICS.Standard.BindsToFunctionLvalue = T2->isFunctionType();
5321+
ICS.Standard.IsImplicitObjectArgumentQualificationConversion = false;
53205322
ICS.Standard.BindsToRvalue = InitCategory.isRValue();
53215323
ICS.Standard.BindsImplicitObjectArgumentWithoutRefQualifier = false;
53225324
ICS.Standard.ObjCLifetimeConversionBinding =
@@ -5496,6 +5498,7 @@ TryReferenceInit(Sema &S, Expr *Init, QualType DeclType,
54965498
ICS.Standard.IsLvalueReference = !isRValRef;
54975499
ICS.Standard.BindsToFunctionLvalue = false;
54985500
ICS.Standard.BindsToRvalue = true;
5501+
ICS.Standard.IsImplicitObjectArgumentQualificationConversion = false;
54995502
ICS.Standard.BindsImplicitObjectArgumentWithoutRefQualifier = false;
55005503
ICS.Standard.ObjCLifetimeConversionBinding = false;
55015504
} else if (ICS.isUserDefined()) {
@@ -5518,6 +5521,8 @@ TryReferenceInit(Sema &S, Expr *Init, QualType DeclType,
55185521
ICS.UserDefined.After.IsLvalueReference = !isRValRef;
55195522
ICS.UserDefined.After.BindsToFunctionLvalue = false;
55205523
ICS.UserDefined.After.BindsToRvalue = !LValRefType;
5524+
ICS.UserDefined.After.IsImplicitObjectArgumentQualificationConversion =
5525+
false;
55215526
ICS.UserDefined.After.BindsImplicitObjectArgumentWithoutRefQualifier = false;
55225527
ICS.UserDefined.After.ObjCLifetimeConversionBinding = false;
55235528
ICS.UserDefined.After.FromBracedInitList = false;
@@ -5802,6 +5807,7 @@ TryListConversion(Sema &S, InitListExpr *From, QualType ToType,
58025807
StandardConversionSequence &SCS = Result.isStandard() ? Result.Standard :
58035808
Result.UserDefined.After;
58045809
SCS.ReferenceBinding = true;
5810+
SCS.IsImplicitObjectArgumentQualificationConversion = false;
58055811
SCS.IsLvalueReference = ToType->isLValueReferenceType();
58065812
SCS.BindsToRvalue = true;
58075813
SCS.BindsToFunctionLvalue = false;
@@ -5999,8 +6005,12 @@ static ImplicitConversionSequence TryObjectArgumentInitialization(
59996005
// affects the conversion rank.
60006006
QualType ClassTypeCanon = S.Context.getCanonicalType(ClassType);
60016007
ImplicitConversionKind SecondKind;
6002-
if (ClassTypeCanon == FromTypeCanon.getLocalUnqualifiedType()) {
6008+
bool IsQualificationConversion = false;
6009+
if (ImplicitParamType.getCanonicalType() == FromTypeCanon) {
60036010
SecondKind = ICK_Identity;
6011+
} else if (ClassTypeCanon == FromTypeCanon.getLocalUnqualifiedType()) {
6012+
SecondKind = ICK_Identity;
6013+
IsQualificationConversion = true;
60046014
} else if (S.IsDerivedFrom(Loc, FromType, ClassType)) {
60056015
SecondKind = ICK_Derived_To_Base;
60066016
} else if (!Method->isExplicitObjectMemberFunction()) {
@@ -6041,6 +6051,8 @@ static ImplicitConversionSequence TryObjectArgumentInitialization(
60416051
ICS.Standard.setFromType(FromType);
60426052
ICS.Standard.setAllToTypes(ImplicitParamType);
60436053
ICS.Standard.ReferenceBinding = true;
6054+
ICS.Standard.IsImplicitObjectArgumentQualificationConversion =
6055+
IsQualificationConversion;
60446056
ICS.Standard.DirectBinding = true;
60456057
ICS.Standard.IsLvalueReference = Method->getRefQualifier() != RQ_RValue;
60466058
ICS.Standard.BindsToFunctionLvalue = false;

clang/test/SemaCXX/overload-resolution-deferred-templates.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,3 +283,31 @@ void f() {
283283
}
284284

285285
#endif
286+
287+
namespace GH147374 {
288+
289+
struct String {};
290+
template <typename T> void operator+(T, String &&) = delete;
291+
292+
struct Bar {
293+
void operator+(String) const; // expected-note {{candidate function}}
294+
friend void operator+(Bar, String) {}; // expected-note {{candidate function}}
295+
};
296+
297+
struct Baz {
298+
void operator+(String); // expected-note {{candidate function}}
299+
friend void operator+(Baz, String) {}; // expected-note {{candidate function}}
300+
};
301+
302+
void test() {
303+
Bar a;
304+
String b;
305+
a + b;
306+
//expected-error@-1 {{use of overloaded operator '+' is ambiguous (with operand types 'Bar' and 'String')}}
307+
308+
Baz z;
309+
z + b;
310+
//expected-error@-1 {{use of overloaded operator '+' is ambiguous (with operand types 'Baz' and 'String')}}
311+
}
312+
313+
}

0 commit comments

Comments
 (0)