Skip to content

Commit d374b65

Browse files
committed
Drop qualifiers from return types in C (DR423)
WG14 DR423 (https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2148.htm#dr_423), resolved during the C11 time frame, changed the way qualifiers are handled on function return types and in cast expressions after it was noted that these types are now directly observable via generic selection expressions. In C, the function declarator is adjusted to ignore all qualifiers (including _Atomic qualifiers). Clang already handles the cast expression case correctly (by performing the lvalue conversion, which drops the qualifiers as well), but with these changes it will now also handle function declarations appropriately. Fixes #39595 Differential Revision: https://reviews.llvm.org/D125919
1 parent 5fc9449 commit d374b65

File tree

12 files changed

+79
-24
lines changed

12 files changed

+79
-24
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,11 @@ AIX Support
340340

341341
C Language Changes in Clang
342342
---------------------------
343+
- Finished implementing support for DR423. We already correctly handled
344+
stripping qualifiers from cast expressions, but we did not strip qualifiers
345+
on function return types. We now properly treat the function as though it
346+
were declarated with an unqualified, non-atomic return type. Fixes
347+
`Issue 39595 <https://github.com/llvm/llvm-project/issues/39595>`_.
343348

344349
C2x Feature Support
345350
-------------------

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6135,9 +6135,6 @@ def note_exits_block_captures_non_trivial_c_struct : Note<
61356135
def note_exits_compound_literal_scope : Note<
61366136
"jump exits lifetime of a compound literal that is non-trivial to destruct">;
61376137

6138-
def err_func_returning_qualified_void : ExtWarn<
6139-
"function cannot return qualified void type %0">,
6140-
InGroup<DiagGroup<"qualified-void-return-type">>;
61416138
def err_func_returning_array_function : Error<
61426139
"function cannot return %select{array|function}0 type %1">;
61436140
def err_field_declared_as_function : Error<"field %0 declared as a function">;

clang/lib/Sema/SemaType.cpp

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5181,15 +5181,11 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
51815181
if ((T.getCVRQualifiers() || T->isAtomicType()) &&
51825182
!(S.getLangOpts().CPlusPlus &&
51835183
(T->isDependentType() || T->isRecordType()))) {
5184-
if (T->isVoidType() && !S.getLangOpts().CPlusPlus &&
5185-
D.getFunctionDefinitionKind() ==
5186-
FunctionDefinitionKind::Definition) {
5187-
// [6.9.1/3] qualified void return is invalid on a C
5188-
// function definition. Apparently ok on declarations and
5189-
// in C++ though (!)
5190-
S.Diag(DeclType.Loc, diag::err_func_returning_qualified_void) << T;
5191-
} else
5192-
diagnoseRedundantReturnTypeQualifiers(S, T, D, chunkIndex);
5184+
// WG14 DR 423 updated 6.7.6.3p4 to have the function declarator drop
5185+
// all qualifiers from the return type.
5186+
diagnoseRedundantReturnTypeQualifiers(S, T, D, chunkIndex);
5187+
if (!S.getLangOpts().CPlusPlus)
5188+
T = T.getAtomicUnqualifiedType();
51935189

51945190
// C++2a [dcl.fct]p12:
51955191
// A volatile-qualified return type is deprecated

clang/test/CodeGen/xcore-stringtype.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ double _Complex Complex; // not supported
4242
// CHECK: !{{[0-9]+}} = !{void ()* @eV, !"f{0}(0)"}
4343
// CHECK: !{{[0-9]+}} = !{void (i32, ...)* @gVA, !"f{0}(si,va)"}
4444
// CHECK: !{{[0-9]+}} = !{void (i32, ...)* @eVA, !"f{0}(si,va)"}
45-
// CHECK: !{{[0-9]+}} = !{i32* (i32*)* @gQ, !"f{crv:p(cv:si)}(p(cv:si))"}
46-
// CHECK: !{{[0-9]+}} = !{i32* (i32*)* @eQ, !"f{crv:p(cv:si)}(p(cv:si))"}
45+
// CHECK: !{{[0-9]+}} = !{i32* (i32*)* @gQ, !"f{p(cv:si)}(p(cv:si))"}
46+
// CHECK: !{{[0-9]+}} = !{i32* (i32*)* @eQ, !"f{p(cv:si)}(p(cv:si))"}
4747
extern void eI();
4848
void gI() {eI();};
4949
extern void eV(void);

clang/test/Sema/block-call.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ int main(void) {
2222

2323
int * const (^IPCC1) () = IPCC;
2424

25-
int * (^IPCC2) () = IPCC; // expected-error {{incompatible block pointer types initializing 'int *(^)()' with an expression of type 'int *const (^)()'}}
25+
int * (^IPCC2) () = IPCC; // OK per WG14 DR 423 because the 'const' was dropped from the declarator.
2626

2727
int (^IPCC3) (const int) = PFR;
2828

@@ -33,7 +33,7 @@ int main(void) {
3333
int (^IPCC6) (int, char (^CArg) (float)) = IPCC4; // expected-error {{incompatible block pointer types initializing 'int (^)(int, char (^)(float))' with an expression of type 'int (^)(int, char (^)(double))'}}
3434

3535
IPCC2 = 0;
36-
IPCC1 = 1; // expected-error {{invalid block pointer conversion assigning to 'int *const (^)()' from 'int'}}
36+
IPCC1 = 1; // expected-error {{invalid block pointer conversion assigning to 'int *(^)()' from 'int'}}
3737
int (^x)() = 0;
3838
int (^y)() = 3; // expected-error {{invalid block pointer conversion initializing 'int (^)()' with an expression of type 'int'}}
3939
int a = 1;

clang/test/Sema/c89.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ const array_of_pointer_to_CI mine3;
107107

108108
void main(void) {} /* expected-error {{'main' must return 'int'}} */
109109

110-
const int main(void) {} /* expected-error {{'main' must return 'int'}} */
110+
const int main(void) {} /* OK per DR 423 */
111111

112112
long long ll1 = /* expected-warning {{'long long' is an extension when C99 mode is not enabled}} */
113113
-42LL; /* expected-warning {{'long long' is an extension when C99 mode is not enabled}} */

clang/test/Sema/function.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,6 @@ void t22(int *ptr, int (*array)[3]) {
117117

118118
void const Bar (void); // ok on decl
119119
// PR 20146
120-
void const Bar (void) // expected-warning {{function cannot return qualified void type 'const void'}}
120+
void const Bar (void) // also okay on defn per DR 423
121121
{
122122
}

clang/test/Sema/warn-missing-prototypes.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,14 @@ const int *get_const() { // expected-warning{{no previous prototype for function
5858

5959
struct MyStruct {};
6060

61+
// FIXME: because qualifiers are ignored in the return type when forming the
62+
// type from the declarator, we get the position incorrect for the fix-it hint.
63+
// It suggests 'const static struct' instead of 'static const struct'. However,
64+
// thanks to the awful rules of parsing in C, the effect is the same and the
65+
// code is valid, if a bit funny looking.
6166
const struct MyStruct get_struct() { // expected-warning{{no previous prototype for function 'get_struct'}}
6267
// expected-note@-1{{declare 'static' if the function is not intended to be used outside of this translation unit}}
63-
// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:1}:"static "
68+
// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:7-[[@LINE-2]]:7}:"static "
6469
struct MyStruct ret;
6570
return ret;
6671
}
@@ -70,7 +75,7 @@ const struct MyStruct get_struct() { // expected-warning{{no previous prototype
7075
// Two spaces between cost and struct
7176
const struct MyStruct get_struct_2() { // expected-warning{{no previous prototype for function 'get_struct_2'}}
7277
// expected-note@-1{{declare 'static' if the function is not intended to be used outside of this translation unit}}
73-
// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:1}:"static "
78+
// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:8-[[@LINE-2]]:8}:"static "
7479
struct MyStruct ret;
7580
return ret;
7681
}

clang/test/Sema/wg14-dr423.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify %s
2+
// RUN: %clang_cc1 -ast-dump %s | FileCheck %s
3+
// expected-no-diagnostics
4+
5+
void GH39595(void) {
6+
// Ensure that qualifiers on function return types are dropped as part of the
7+
// declaration.
8+
extern const int const_int(void);
9+
// CHECK: FunctionDecl {{.*}} parent {{.*}} <col:3, col:34> col:20 referenced const_int 'int (void)' extern
10+
extern _Atomic int atomic(void);
11+
// CHECK: FunctionDecl {{.*}} parent {{.*}} <col:3, col:33> col:22 referenced atomic 'int (void)' extern
12+
13+
(void)_Generic(const_int(), int : 1);
14+
(void)_Generic(atomic(), int : 1);
15+
16+
// Make sure they're dropped from function pointers as well.
17+
_Atomic int (*fp)(void);
18+
(void)_Generic(fp(), int : 1);
19+
}
20+
21+
void casting(void) {
22+
// Ensure that qualifiers on cast operations are also dropped.
23+
(void)_Generic((const int)12, int : 1);
24+
25+
struct S { int i; } s;
26+
(void)_Generic((const struct S)s, struct S : 1);
27+
28+
int i;
29+
__typeof__((const int)i) j;
30+
j = 100; // If we didn't strip the qualifiers during the cast, this would err.
31+
}

clang/test/SemaObjC/block-omitted-return-type.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ - (void)test
2323
void (^simpleBlock4)(void) = ^ const { //expected-warning {{'const' qualifier on omitted return type '<dependent type>' has no effect}}
2424
return;
2525
};
26-
void (^simpleBlock5)(void) = ^ const void { //expected-error {{incompatible block pointer types initializing 'void (^)(void)' with an expression of type 'const void (^)(void)'}}
27-
return; // expected-warning@-1 {{function cannot return qualified void type 'const void'}}
26+
void (^simpleBlock5)(void) = ^ const void { // OK after DR 423.
27+
return;
2828
};
2929
void (^simpleBlock6)(void) = ^ const (void) { //expected-warning {{'const' qualifier on omitted return type '<dependent type>' has no effect}}
3030
return;

clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2095,8 +2095,18 @@ TEST_P(ASTMatchersTest, QualifiedTypeLocTest_BindsToConstIntVarDecl) {
20952095
}
20962096

20972097
TEST_P(ASTMatchersTest, QualifiedTypeLocTest_BindsToConstIntFunctionDecl) {
2098-
EXPECT_TRUE(matches("const int f() { return 5; }",
2099-
qualifiedTypeLoc(loc(asString("const int")))));
2098+
StringRef Code = R"(
2099+
const int f() { return 5; }
2100+
)";
2101+
// In C++, the qualified return type is retained.
2102+
EXPECT_TRUE(matchesConditionally(
2103+
Code, qualifiedTypeLoc(loc(asString("const int"))), true, langAnyCxx()));
2104+
// In C, the qualifications on the return type are dropped, so we expect it
2105+
// to match 'int' rather than 'const int'.
2106+
EXPECT_TRUE(matchesConditionally(
2107+
Code, qualifiedTypeLoc(loc(asString("const int"))), false, langAnyC()));
2108+
EXPECT_TRUE(
2109+
matchesConditionally(Code, loc(asString("int")), true, langAnyC()));
21002110
}
21012111

21022112
TEST_P(ASTMatchersTest, QualifiedTypeLocTest_DoesNotBindToUnqualifiedVarDecl) {

clang/unittests/ASTMatchers/ASTMatchersTest.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,17 @@ class VerifyMatch : public MatchFinder::MatchCallback {
6060
const std::unique_ptr<BoundNodesCallback> FindResultReviewer;
6161
};
6262

63+
inline ArrayRef<TestLanguage> langAnyC() {
64+
static const TestLanguage Result[] = {Lang_C89, Lang_C99};
65+
return ArrayRef<TestLanguage>(Result);
66+
}
67+
68+
inline ArrayRef<TestLanguage> langAnyCxx() {
69+
static const TestLanguage Result[] = {Lang_CXX03, Lang_CXX11, Lang_CXX14,
70+
Lang_CXX17, Lang_CXX20};
71+
return ArrayRef<TestLanguage>(Result);
72+
}
73+
6374
inline ArrayRef<TestLanguage> langCxx11OrLater() {
6475
static const TestLanguage Result[] = {Lang_CXX11, Lang_CXX14, Lang_CXX17,
6576
Lang_CXX20};

0 commit comments

Comments
 (0)