Skip to content

Commit 8f25c0b

Browse files
authored
[clang] Fix covariant cv-qualification check to require the override function return type to have the same or less cv-qualification (#112713)
This prevents changing cv-qualification from const to volatile or vice versa, for example. https://eel.is/c++draft/class.virtual#8.3 Previously, we checked that the new type is the same or more qualified to return an error, but the standard requires the new type to be the same or less qualified and since the cv-qualification is only partially ordered, we cannot rely on a check on whether it is more qualified to return an error. Now, we reversed the condition to check whether the old is at least as qualified, and return an error if it is not. Also, adjusted the error name and message to clarify the requirement and added a missing closing parenthesis. Added tests to cover different use cases for classes with different qualifications and also refactored them to make them easier to follow: 1. Use override to make sure the function names actually match. 2. Named the function in a more descriptive way to clarify what each use case is checking. Fixes: #111742
1 parent caa32e6 commit 8f25c0b

File tree

4 files changed

+57
-14
lines changed

4 files changed

+57
-14
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,17 +99,24 @@ C++ Specific Potentially Breaking Changes
9999
// Was error, now evaluates to false.
100100
constexpr bool b = f() == g();
101101
102-
- Clang will now correctly not consider pointers to non classes for covariance.
102+
- Clang will now correctly not consider pointers to non classes for covariance
103+
and disallow changing return type to a type that doesn't have the same or less cv-qualifications.
103104

104105
.. code-block:: c++
105106

106107
struct A {
107108
virtual const int *f() const;
109+
virtual const std::string *g() const;
108110
};
109111
struct B : A {
110112
// Return type has less cv-qualification but doesn't point to a class.
111113
// Error will be generated.
112114
int *f() const override;
115+
116+
// Return type doesn't have more cv-qualification also not the same or
117+
// less cv-qualification.
118+
// Error will be generated.
119+
volatile std::string *g() const override;
113120
};
114121
115122
- The warning ``-Wdeprecated-literal-operator`` is now on by default, as this is

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2182,10 +2182,10 @@ def err_covariant_return_incomplete : Error<
21822182
def err_covariant_return_type_different_qualifications : Error<
21832183
"return type of virtual function %0 is not covariant with the return type of "
21842184
"the function it overrides (%1 has different qualifiers than %2)">;
2185-
def err_covariant_return_type_class_type_more_qualified : Error<
2185+
def err_covariant_return_type_class_type_not_same_or_less_qualified : Error<
21862186
"return type of virtual function %0 is not covariant with the return type of "
2187-
"the function it overrides (class type %1 is more qualified than class "
2188-
"type %2">;
2187+
"the function it overrides (class type %1 does not have the same "
2188+
"cv-qualification as or less cv-qualification than class type %2)">;
21892189

21902190
// C++ implicit special member functions
21912191
def note_in_declaration_of_implicit_special_member : Note<

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18338,9 +18338,9 @@ bool Sema::CheckOverridingFunctionReturnType(const CXXMethodDecl *New,
1833818338

1833918339

1834018340
// The new class type must have the same or less qualifiers as the old type.
18341-
if (NewClassTy.isMoreQualifiedThan(OldClassTy)) {
18341+
if (!OldClassTy.isAtLeastAsQualifiedAs(NewClassTy)) {
1834218342
Diag(New->getLocation(),
18343-
diag::err_covariant_return_type_class_type_more_qualified)
18343+
diag::err_covariant_return_type_class_type_not_same_or_less_qualified)
1834418344
<< New->getDeclName() << NewTy << OldTy
1834518345
<< New->getReturnTypeSourceRange();
1834618346
Diag(Old->getLocation(), diag::note_overridden_virtual_function)

clang/test/SemaCXX/virtual-override.cpp

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -83,17 +83,53 @@ namespace T6 {
8383
struct a { };
8484

8585
class A {
86-
virtual const a* f();
87-
virtual a* g(); // expected-note{{overridden virtual function is here}}
88-
virtual const int* h(); // expected-note{{overridden virtual function is here}}
89-
virtual int* i(); // expected-note{{overridden virtual function is here}}
86+
// Classes.
87+
virtual const a* const_vs_unqualified_class();
88+
virtual a* unqualified_vs_const_class(); // expected-note{{overridden virtual function is here}}
89+
90+
virtual volatile a* volatile_vs_unqualified_class();
91+
virtual a* unqualified_vs_volatile_class(); // expected-note{{overridden virtual function is here}}
92+
93+
virtual const a* const_vs_volatile_class(); // expected-note{{overridden virtual function is here}}
94+
virtual volatile a* volatile_vs_const_class(); // expected-note{{overridden virtual function is here}}
95+
96+
virtual const volatile a* const_volatile_vs_const_class();
97+
virtual const a* const_vs_const_volatile_class(); // expected-note{{overridden virtual function is here}}
98+
99+
virtual const volatile a* const_volatile_vs_volatile_class();
100+
virtual volatile a* volatile_vs_const_volatile_class(); // expected-note{{overridden virtual function is here}}
101+
102+
virtual const volatile a* const_volatile_vs_unualified_class();
103+
virtual a* unqualified_vs_const_volatile_class(); // expected-note{{overridden virtual function is here}}
104+
105+
// Non Classes.
106+
virtual const int* const_vs_unqualified_non_class(); // expected-note{{overridden virtual function is here}}
107+
virtual int* unqualified_vs_const_non_class(); // expected-note{{overridden virtual function is here}}
90108
};
91109

92110
class B : A {
93-
virtual a* f();
94-
virtual const a* g(); // expected-error{{return type of virtual function 'g' is not covariant with the return type of the function it overrides (class type 'const a *' is more qualified than class type 'a *'}}
95-
virtual int* h(); // expected-error{{virtual function 'h' has a different return type ('int *') than the function it overrides (which has return type 'const int *')}}
96-
virtual const int* i(); // expected-error{{virtual function 'i' has a different return type ('const int *') than the function it overrides (which has return type 'int *')}}
111+
// Classes.
112+
a* const_vs_unqualified_class() override;
113+
const a* unqualified_vs_const_class() override; // expected-error{{return type of virtual function 'unqualified_vs_const_class' is not covariant with the return type of the function it overrides (class type 'const a *' does not have the same cv-qualification as or less cv-qualification than class type 'a *')}}
114+
115+
a* volatile_vs_unqualified_class() override;
116+
volatile a* unqualified_vs_volatile_class() override; // expected-error{{return type of virtual function 'unqualified_vs_volatile_class' is not covariant with the return type of the function it overrides (class type 'volatile a *' does not have the same cv-qualification as or less cv-qualification than class type 'a *')}}
117+
118+
volatile a* const_vs_volatile_class() override; // expected-error{{return type of virtual function 'const_vs_volatile_class' is not covariant with the return type of the function it overrides (class type 'volatile a *' does not have the same cv-qualification as or less cv-qualification than class type 'const a *')}}
119+
const a* volatile_vs_const_class() override; // expected-error{{return type of virtual function 'volatile_vs_const_class' is not covariant with the return type of the function it overrides (class type 'const a *' does not have the same cv-qualification as or less cv-qualification than class type 'volatile a *')}}
120+
121+
const a* const_volatile_vs_const_class() override;
122+
const volatile a* const_vs_const_volatile_class() override; // expected-error{{return type of virtual function 'const_vs_const_volatile_class' is not covariant with the return type of the function it overrides (class type 'const volatile a *' does not have the same cv-qualification as or less cv-qualification than class type 'const a *')}}
123+
124+
volatile a* const_volatile_vs_volatile_class() override;
125+
const volatile a* volatile_vs_const_volatile_class() override; // expected-error{{return type of virtual function 'volatile_vs_const_volatile_class' is not covariant with the return type of the function it overrides (class type 'const volatile a *' does not have the same cv-qualification as or less cv-qualification than class type 'volatile a *')}}
126+
127+
a* const_volatile_vs_unualified_class() override;
128+
const volatile a* unqualified_vs_const_volatile_class() override; // expected-error{{return type of virtual function 'unqualified_vs_const_volatile_class' is not covariant with the return type of the function it overrides (class type 'const volatile a *' does not have the same cv-qualification as or less cv-qualification than class type 'a *')}}
129+
130+
// Non Classes.
131+
int* const_vs_unqualified_non_class() override; // expected-error{{virtual function 'const_vs_unqualified_non_class' has a different return type ('int *') than the function it overrides (which has return type 'const int *')}}
132+
const int* unqualified_vs_const_non_class() override; // expected-error{{virtual function 'unqualified_vs_const_non_class' has a different return type ('const int *') than the function it overrides (which has return type 'int *')}}
97133
};
98134

99135
}

0 commit comments

Comments
 (0)