mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-19 01:46:49 +00:00
[C2x] Disallow functions without prototypes/functions with identifier lists
WG14 has elected to remove support for K&R C functions in C2x. The feature was introduced into C89 already deprecated, so after this long of a deprecation period, the committee has made an empty parameter list mean the same thing in C as it means in C++: the function accepts no arguments exactly as if the function were written with (void) as the parameter list. This patch implements WG14 N2841 No function declarators without prototypes (http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2841.htm) and WG14 N2432 Remove support for function definitions with identifier lists (http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2432.pdf). It also adds The -fno-knr-functions command line option to opt into this behavior in other language modes. Differential Revision: https://reviews.llvm.org/D123955
This commit is contained in:
parent
9c069374ce
commit
9955f14aaf
@ -176,6 +176,11 @@ Non-comprehensive list of changes in this release
|
||||
|
||||
New Compiler Flags
|
||||
------------------
|
||||
- Added the ``-fno-knr-functions`` flag to allow users to opt into the C2x
|
||||
behavior where a function with an empty parameter list is treated as though
|
||||
the parameter list were ``void``. There is no ``-fknr-functions`` or
|
||||
``-fno-no-knr-functions`` flag; this feature cannot be disabled in language
|
||||
modes where it is required, such as C++ or C2x.
|
||||
|
||||
Deprecated Compiler Flags
|
||||
-------------------------
|
||||
@ -239,6 +244,8 @@ C2x Feature Support
|
||||
- Removed support for implicit function declarations. This was a C89 feature
|
||||
that was removed in C99, but cannot be supported in C2x because it requires
|
||||
support for functions without prototypes, which no longer exist in C2x.
|
||||
- Implemented `WG14 N2841 No function declarators without prototypes <https://www9.open-std.org/jtc1/sc22/wg14/www/docs/n2841.htm>`_
|
||||
and `WG14 N2432 Remove support for function definitions with identifier lists <https://www9.open-std.org/jtc1/sc22/wg14/www/docs/n2432.pdf>`_.
|
||||
|
||||
C++ Language Changes in Clang
|
||||
-----------------------------
|
||||
|
@ -121,6 +121,7 @@ LANGOPT(GNUMode , 1, 1, "GNU extensions")
|
||||
LANGOPT(GNUKeywords , 1, 1, "GNU keywords")
|
||||
VALUE_LANGOPT(GNUCVersion , 32, 0, "GNU C compatibility version")
|
||||
BENIGN_LANGOPT(ImplicitInt, 1, 0, "C89 implicit 'int'")
|
||||
LANGOPT(DisableKNRFunctions, 1, 0, "require function types to have a prototype")
|
||||
LANGOPT(Digraphs , 1, 0, "digraphs")
|
||||
BENIGN_LANGOPT(HexFloats , 1, 0, "C99 hexadecimal float constants")
|
||||
LANGOPT(CXXOperatorNames , 1, 0, "C++ operator name keywords")
|
||||
|
@ -521,6 +521,12 @@ public:
|
||||
/// as a string.
|
||||
std::string getOpenCLVersionString() const;
|
||||
|
||||
/// Returns true if functions without prototypes or functions with an
|
||||
/// identifier list (aka K&R C functions) are not allowed.
|
||||
bool requiresStrictPrototypes() const {
|
||||
return CPlusPlus || C2x || DisableKNRFunctions;
|
||||
}
|
||||
|
||||
/// Check if return address signing is enabled.
|
||||
bool hasSignReturnAddress() const {
|
||||
return getSignReturnAddressScope() != SignReturnAddressScopeKind::None;
|
||||
|
@ -2297,6 +2297,11 @@ defm implicit_modules : BoolFOption<"implicit-modules",
|
||||
def fretain_comments_from_system_headers : Flag<["-"], "fretain-comments-from-system-headers">, Group<f_Group>, Flags<[CC1Option]>,
|
||||
MarshallingInfoFlag<LangOpts<"RetainCommentsFromSystemHeaders">>;
|
||||
|
||||
def fno_knr_functions : Flag<["-"], "fno-knr-functions">, Group<f_Group>,
|
||||
MarshallingInfoFlag<LangOpts<"DisableKNRFunctions">>,
|
||||
HelpText<"Disable support for K&R C function declarations">,
|
||||
Flags<[CC1Option, CoreOption]>;
|
||||
|
||||
def fmudflapth : Flag<["-"], "fmudflapth">, Group<f_Group>;
|
||||
def fmudflap : Flag<["-"], "fmudflap">, Group<f_Group>;
|
||||
def fnested_functions : Flag<["-"], "fnested-functions">, Group<f_Group>;
|
||||
|
@ -4239,6 +4239,13 @@ static bool isCanonicalResultType(QualType T) {
|
||||
QualType
|
||||
ASTContext::getFunctionNoProtoType(QualType ResultTy,
|
||||
const FunctionType::ExtInfo &Info) const {
|
||||
// FIXME: This assertion cannot be enabled (yet) because the ObjC rewriter
|
||||
// functionality creates a function without a prototype regardless of
|
||||
// language mode (so it makes them even in C++). Once the rewriter has been
|
||||
// fixed, this assertion can be enabled again.
|
||||
//assert(!LangOpts.requiresStrictPrototypes() &&
|
||||
// "strict prototypes are disabled");
|
||||
|
||||
// Unique functions, to guarantee there is only one function of a particular
|
||||
// structure.
|
||||
llvm::FoldingSetNodeID ID;
|
||||
@ -11235,7 +11242,7 @@ QualType ASTContext::GetBuiltinType(unsigned Id,
|
||||
|
||||
|
||||
// We really shouldn't be making a no-proto type here.
|
||||
if (ArgTypes.empty() && Variadic && !getLangOpts().CPlusPlus)
|
||||
if (ArgTypes.empty() && Variadic && !getLangOpts().requiresStrictPrototypes())
|
||||
return getFunctionNoProtoType(ResType, EI);
|
||||
|
||||
FunctionProtoType::ExtProtoInfo EPI;
|
||||
|
@ -6661,8 +6661,11 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
|
||||
else if (RequiresArg)
|
||||
Diag(Tok, diag::err_argument_required_after_attribute);
|
||||
|
||||
HasProto = ParamInfo.size() || getLangOpts().CPlusPlus
|
||||
|| getLangOpts().OpenCL;
|
||||
// OpenCL disallows functions without a prototype, but it doesn't enforce
|
||||
// strict prototypes as in C2x because it allows a function definition to
|
||||
// have an identifier list. See OpenCL 3.0 6.11/g for more details.
|
||||
HasProto = ParamInfo.size() || getLangOpts().requiresStrictPrototypes() ||
|
||||
getLangOpts().OpenCL;
|
||||
|
||||
// If we have the closing ')', eat it.
|
||||
Tracker.consumeClose();
|
||||
@ -6799,7 +6802,7 @@ bool Parser::ParseRefQualifier(bool &RefQualifierIsLValueRef,
|
||||
/// Note that identifier-lists are only allowed for normal declarators, not for
|
||||
/// abstract-declarators.
|
||||
bool Parser::isFunctionDeclaratorIdentifierList() {
|
||||
return !getLangOpts().CPlusPlus
|
||||
return !getLangOpts().requiresStrictPrototypes()
|
||||
&& Tok.is(tok::identifier)
|
||||
&& !TryAltiVecVectorToken()
|
||||
// K&R identifier lists can't have typedefs as identifiers, per C99
|
||||
@ -6833,6 +6836,10 @@ bool Parser::isFunctionDeclaratorIdentifierList() {
|
||||
void Parser::ParseFunctionDeclaratorIdentifierList(
|
||||
Declarator &D,
|
||||
SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo) {
|
||||
// We should never reach this point in C2x or C++.
|
||||
assert(!getLangOpts().requiresStrictPrototypes() &&
|
||||
"Cannot parse an identifier list in C2x or C++");
|
||||
|
||||
// If there was no identifier specified for the declarator, either we are in
|
||||
// an abstract-declarator, or we are in a parameter declarator which was found
|
||||
// to be abstract. In abstract-declarators, identifier lists are not valid:
|
||||
|
@ -8822,6 +8822,9 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D,
|
||||
(D.getDeclSpec().isTypeRep() &&
|
||||
D.getDeclSpec().getRepAsType().get()->isFunctionProtoType()) ||
|
||||
(!R->getAsAdjusted<FunctionType>() && R->isFunctionProtoType());
|
||||
assert(
|
||||
(HasPrototype || !SemaRef.getLangOpts().requiresStrictPrototypes()) &&
|
||||
"Strict prototypes are required");
|
||||
|
||||
NewFD = FunctionDecl::Create(
|
||||
SemaRef.Context, DC, D.getBeginLoc(), NameInfo, R, TInfo, SC,
|
||||
|
@ -5270,8 +5270,11 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
|
||||
FunctionType::ExtInfo EI(
|
||||
getCCForDeclaratorChunk(S, D, DeclType.getAttrs(), FTI, chunkIndex));
|
||||
|
||||
if (!FTI.NumParams && !FTI.isVariadic && !LangOpts.CPlusPlus
|
||||
&& !LangOpts.OpenCL) {
|
||||
// OpenCL disallows functions without a prototype, but it doesn't enforce
|
||||
// strict prototypes as in C2x because it allows a function definition to
|
||||
// have an identifier list. See OpenCL 3.0 6.11/g for more details.
|
||||
if (!FTI.NumParams && !FTI.isVariadic &&
|
||||
!LangOpts.requiresStrictPrototypes() && !LangOpts.OpenCL) {
|
||||
// Simple void foo(), where the incoming T is the result type.
|
||||
T = Context.getFunctionNoProtoType(T, EI);
|
||||
} else {
|
||||
@ -5290,8 +5293,10 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
|
||||
S.Diag(FTI.Params[0].IdentLoc,
|
||||
diag::err_ident_list_in_fn_declaration);
|
||||
D.setInvalidType(true);
|
||||
// Recover by creating a K&R-style function type.
|
||||
T = Context.getFunctionNoProtoType(T, EI);
|
||||
// Recover by creating a K&R-style function type, if possible.
|
||||
T = (!LangOpts.requiresStrictPrototypes() && !LangOpts.OpenCL)
|
||||
? Context.getFunctionNoProtoType(T, EI)
|
||||
: Context.IntTy;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1033,8 +1033,10 @@ const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D,
|
||||
T = TSI->getType();
|
||||
if (T.isNull())
|
||||
T = getContext().VoidTy;
|
||||
if (!T->getAs<FunctionType>())
|
||||
T = getContext().getFunctionNoProtoType(T);
|
||||
if (!T->getAs<FunctionType>()) {
|
||||
FunctionProtoType::ExtProtoInfo Ext;
|
||||
T = getContext().getFunctionType(T, None, Ext);
|
||||
}
|
||||
T = getContext().getBlockPointerType(T);
|
||||
|
||||
const BlockCodeRegion *BTR =
|
||||
|
11
clang/test/Driver/no-knr-functions.c
Normal file
11
clang/test/Driver/no-knr-functions.c
Normal file
@ -0,0 +1,11 @@
|
||||
// Ensure that we cannot disable strict prototypes, no matter how hard we try.
|
||||
|
||||
// RUN: not %clang -fno-no-knr-functions -x c++ %s 2>&1 | FileCheck --check-prefixes=NONO %s
|
||||
// RUN: not %clang -fno-no-knr-functions -x c %s 2>&1 | FileCheck --check-prefixes=NONO %s
|
||||
// RUN: not %clang -fno-no-knr-functions -std=c89 -x c %s 2>&1 | FileCheck --check-prefixes=NONO %s
|
||||
// RUN: not %clang -fknr-functions -x c++ %s 2>&1 | FileCheck --check-prefixes=POS %s
|
||||
// RUN: not %clang -fknr-functions -x c %s 2>&1 | FileCheck --check-prefixes=POS %s
|
||||
// RUN: not %clang -fknr-functions -std=c89 -x c %s 2>&1 | FileCheck --check-prefixes=POS %s
|
||||
|
||||
// NONO: error: unknown argument: '-fno-no-knr-functions'
|
||||
// POS: error: unknown argument: '-fknr-functions'
|
11
clang/test/Frontend/no-knr-functions.c
Normal file
11
clang/test/Frontend/no-knr-functions.c
Normal file
@ -0,0 +1,11 @@
|
||||
// Ensure that we cannot disable strict prototypes, no matter how hard we try.
|
||||
|
||||
// RUN: not %clang_cc1 -fno-no-knr-functions -x c++ %s 2>&1 | FileCheck --check-prefixes=NONO %s
|
||||
// RUN: not %clang_cc1 -fno-no-knr-functions -x c %s 2>&1 | FileCheck --check-prefixes=NONO %s
|
||||
// RUN: not %clang_cc1 -fno-no-knr-functions -std=c89 -x c %s 2>&1 | FileCheck --check-prefixes=NONO %s
|
||||
// RUN: not %clang_cc1 -fknr-functions -x c++ %s 2>&1 | FileCheck --check-prefixes=POS %s
|
||||
// RUN: not %clang_cc1 -fknr-functions -x c %s 2>&1 | FileCheck --check-prefixes=POS %s
|
||||
// RUN: not %clang_cc1 -fknr-functions -std=c89 -x c %s 2>&1 | FileCheck --check-prefixes=POS %s
|
||||
|
||||
// NONO: error: unknown argument: '-fno-no-knr-functions'
|
||||
// POS: error: unknown argument: '-fknr-functions'
|
@ -1,5 +1,5 @@
|
||||
// RUN: %clang_cc1 -fsyntax-only -fdouble-square-bracket-attributes -verify -Wno-strict-prototypes %s
|
||||
// RUN: %clang_cc1 -fsyntax-only -std=gnu2x -verify -Wno-strict-prototypes %s
|
||||
// RUN: %clang_cc1 -fsyntax-only -fdouble-square-bracket-attributes -verify=expected,notc2x -Wno-strict-prototypes %s
|
||||
// RUN: %clang_cc1 -fsyntax-only -std=gnu2x -verify=expected,c2x %s
|
||||
|
||||
enum [[]] E {
|
||||
One [[]],
|
||||
@ -59,7 +59,10 @@ void f4(void) [[]];
|
||||
|
||||
void f5(int i [[]], [[]] int j, int [[]] k);
|
||||
|
||||
void f6(a, b) [[]] int a; int b; { // expected-error {{an attribute list cannot appear here}}
|
||||
void f6(a, b) [[]] int a; int b; { // notc2x-error {{an attribute list cannot appear here}} \
|
||||
c2x-warning 2 {{type specifier missing, defaults to 'int'}} \
|
||||
c2x-error {{expected ';' after top level declarator}} \
|
||||
c2x-error {{expected identifier or '('}}
|
||||
}
|
||||
|
||||
// FIXME: technically, an attribute list cannot appear here, but we currently
|
||||
@ -67,7 +70,10 @@ void f6(a, b) [[]] int a; int b; { // expected-error {{an attribute list cannot
|
||||
// behavior given that we *don't* want to parse it as part of the K&R parameter
|
||||
// declarations. It is disallowed to avoid a parsing ambiguity we already
|
||||
// handle well.
|
||||
int (*f7(a, b))(int, int) [[]] int a; int b; {
|
||||
int (*f7(a, b))(int, int) [[]] int a; int b; { // c2x-warning 2 {{type specifier missing, defaults to 'int'}} \
|
||||
c2x-error {{expected ';' after top level declarator}} \
|
||||
c2x-error {{expected identifier or '('}}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
16
clang/test/Parser/c2x-func-prototype.c
Normal file
16
clang/test/Parser/c2x-func-prototype.c
Normal file
@ -0,0 +1,16 @@
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify=c2x -std=c2x %s
|
||||
// RUN: %clang_cc1 -Wno-strict-prototypes -fsyntax-only -verify -std=c17 %s
|
||||
// expected-no-diagnostics
|
||||
|
||||
// Functions with an identifier list are not supported in C2x.
|
||||
void ident_list(a) // c2x-error {{expected ';' after top level declarator}} \
|
||||
c2x-warning {{type specifier missing, defaults to 'int'}}
|
||||
int a;
|
||||
{} // c2x-error {{expected identifier or '('}}
|
||||
|
||||
// Functions with an empty parameter list are supported as though the function
|
||||
// was declared with a parameter list of (void). Ensure they still parse.
|
||||
void no_param_decl();
|
||||
void no_param_defn() {}
|
||||
void (*var_of_type_with_no_param)();
|
||||
typedef void fn();
|
@ -20,7 +20,7 @@ void context_async_okay(void *context [[clang::swift_async_context]]) [[clang::s
|
||||
void context_async_okay2(void *context [[clang::swift_async_context]], void *selfType, char **selfWitnessTable) [[clang::swiftasynccall]];
|
||||
|
||||
[[clang::ownership_returns(foo)]] void *f1(void);
|
||||
[[clang::ownership_returns(foo)]] void *f2(); // expected-warning {{'ownership_returns' attribute only applies to non-K&R-style functions}}
|
||||
[[clang::ownership_returns(foo)]] void *f2();
|
||||
|
||||
[[clang::unavailable("not available - replaced")]] void foo2(void); // expected-note {{'foo2' has been explicitly marked unavailable here}}
|
||||
void bar(void) {
|
||||
|
24
clang/test/Sema/c2x-func-prototype.c
Normal file
24
clang/test/Sema/c2x-func-prototype.c
Normal file
@ -0,0 +1,24 @@
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify=c2x -std=c2x %s
|
||||
// RUN: %clang_cc1 -Wno-strict-prototypes -fsyntax-only -verify -std=c17 %s
|
||||
// RUN: %clang_cc1 -fsyntax-only -fno-knr-functions -std=c99 -verify=c2x %s
|
||||
// expected-no-diagnostics
|
||||
|
||||
void func(); // c2x-note {{'func' declared here}}
|
||||
typedef void (*fp)();
|
||||
|
||||
void other_func(int i);
|
||||
|
||||
void call(void) {
|
||||
func(1, 2, 3); // c2x-error {{too many arguments to function call, expected 0, have 3}}
|
||||
fp call_me = func;
|
||||
call_me(1, 2, 3); // c2x-error {{too many arguments to function call, expected 0, have 3}}
|
||||
|
||||
fp nope = other_func; // c2x-warning {{incompatible function pointer types initializing 'fp' (aka 'void (*)(void)') with an expression of type 'void (int)'}}
|
||||
}
|
||||
|
||||
// Ensure these function declarations do not merge in C2x.
|
||||
void redecl1(); // c2x-note {{previous declaration is here}}
|
||||
void redecl1(int i); // c2x-error {{conflicting types for 'redecl1'}}
|
||||
|
||||
void redecl2(int i); // c2x-note {{previous declaration is here}}
|
||||
void redecl2(); // c2x-error {{conflicting types for 'redecl2'}}
|
@ -725,7 +725,7 @@ conformance.</p>
|
||||
<tr>
|
||||
<td>Remove support for function definitions with identifier lists</td>
|
||||
<td><a href="http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2432.pdf">N2432</a></td>
|
||||
<td class="none" align="center">No</td>
|
||||
<td class="unreleased" align="center">Clang 15</td>
|
||||
</tr>
|
||||
<!-- Oct 2019 Papers -->
|
||||
<tr>
|
||||
|
Loading…
x
Reference in New Issue
Block a user