Skip to content

Commit 9955f14

Browse files
committed
[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
1 parent 9c06937 commit 9955f14

File tree

16 files changed

+127
-16
lines changed

16 files changed

+127
-16
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,11 @@ Non-comprehensive list of changes in this release
176176

177177
New Compiler Flags
178178
------------------
179+
- Added the ``-fno-knr-functions`` flag to allow users to opt into the C2x
180+
behavior where a function with an empty parameter list is treated as though
181+
the parameter list were ``void``. There is no ``-fknr-functions`` or
182+
``-fno-no-knr-functions`` flag; this feature cannot be disabled in language
183+
modes where it is required, such as C++ or C2x.
179184

180185
Deprecated Compiler Flags
181186
-------------------------
@@ -239,6 +244,8 @@ C2x Feature Support
239244
- Removed support for implicit function declarations. This was a C89 feature
240245
that was removed in C99, but cannot be supported in C2x because it requires
241246
support for functions without prototypes, which no longer exist in C2x.
247+
- Implemented `WG14 N2841 No function declarators without prototypes <https://www9.open-std.org/jtc1/sc22/wg14/www/docs/n2841.htm>`_
248+
and `WG14 N2432 Remove support for function definitions with identifier lists <https://www9.open-std.org/jtc1/sc22/wg14/www/docs/n2432.pdf>`_.
242249

243250
C++ Language Changes in Clang
244251
-----------------------------

clang/include/clang/Basic/LangOptions.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ LANGOPT(GNUMode , 1, 1, "GNU extensions")
121121
LANGOPT(GNUKeywords , 1, 1, "GNU keywords")
122122
VALUE_LANGOPT(GNUCVersion , 32, 0, "GNU C compatibility version")
123123
BENIGN_LANGOPT(ImplicitInt, 1, 0, "C89 implicit 'int'")
124+
LANGOPT(DisableKNRFunctions, 1, 0, "require function types to have a prototype")
124125
LANGOPT(Digraphs , 1, 0, "digraphs")
125126
BENIGN_LANGOPT(HexFloats , 1, 0, "C99 hexadecimal float constants")
126127
LANGOPT(CXXOperatorNames , 1, 0, "C++ operator name keywords")

clang/include/clang/Basic/LangOptions.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,12 @@ class LangOptions : public LangOptionsBase {
521521
/// as a string.
522522
std::string getOpenCLVersionString() const;
523523

524+
/// Returns true if functions without prototypes or functions with an
525+
/// identifier list (aka K&R C functions) are not allowed.
526+
bool requiresStrictPrototypes() const {
527+
return CPlusPlus || C2x || DisableKNRFunctions;
528+
}
529+
524530
/// Check if return address signing is enabled.
525531
bool hasSignReturnAddress() const {
526532
return getSignReturnAddressScope() != SignReturnAddressScopeKind::None;

clang/include/clang/Driver/Options.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2297,6 +2297,11 @@ defm implicit_modules : BoolFOption<"implicit-modules",
22972297
def fretain_comments_from_system_headers : Flag<["-"], "fretain-comments-from-system-headers">, Group<f_Group>, Flags<[CC1Option]>,
22982298
MarshallingInfoFlag<LangOpts<"RetainCommentsFromSystemHeaders">>;
22992299

2300+
def fno_knr_functions : Flag<["-"], "fno-knr-functions">, Group<f_Group>,
2301+
MarshallingInfoFlag<LangOpts<"DisableKNRFunctions">>,
2302+
HelpText<"Disable support for K&R C function declarations">,
2303+
Flags<[CC1Option, CoreOption]>;
2304+
23002305
def fmudflapth : Flag<["-"], "fmudflapth">, Group<f_Group>;
23012306
def fmudflap : Flag<["-"], "fmudflap">, Group<f_Group>;
23022307
def fnested_functions : Flag<["-"], "fnested-functions">, Group<f_Group>;

clang/lib/AST/ASTContext.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4239,6 +4239,13 @@ static bool isCanonicalResultType(QualType T) {
42394239
QualType
42404240
ASTContext::getFunctionNoProtoType(QualType ResultTy,
42414241
const FunctionType::ExtInfo &Info) const {
4242+
// FIXME: This assertion cannot be enabled (yet) because the ObjC rewriter
4243+
// functionality creates a function without a prototype regardless of
4244+
// language mode (so it makes them even in C++). Once the rewriter has been
4245+
// fixed, this assertion can be enabled again.
4246+
//assert(!LangOpts.requiresStrictPrototypes() &&
4247+
// "strict prototypes are disabled");
4248+
42424249
// Unique functions, to guarantee there is only one function of a particular
42434250
// structure.
42444251
llvm::FoldingSetNodeID ID;
@@ -11235,7 +11242,7 @@ QualType ASTContext::GetBuiltinType(unsigned Id,
1123511242

1123611243

1123711244
// We really shouldn't be making a no-proto type here.
11238-
if (ArgTypes.empty() && Variadic && !getLangOpts().CPlusPlus)
11245+
if (ArgTypes.empty() && Variadic && !getLangOpts().requiresStrictPrototypes())
1123911246
return getFunctionNoProtoType(ResType, EI);
1124011247

1124111248
FunctionProtoType::ExtProtoInfo EPI;

clang/lib/Parse/ParseDecl.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6661,8 +6661,11 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
66616661
else if (RequiresArg)
66626662
Diag(Tok, diag::err_argument_required_after_attribute);
66636663

6664-
HasProto = ParamInfo.size() || getLangOpts().CPlusPlus
6665-
|| getLangOpts().OpenCL;
6664+
// OpenCL disallows functions without a prototype, but it doesn't enforce
6665+
// strict prototypes as in C2x because it allows a function definition to
6666+
// have an identifier list. See OpenCL 3.0 6.11/g for more details.
6667+
HasProto = ParamInfo.size() || getLangOpts().requiresStrictPrototypes() ||
6668+
getLangOpts().OpenCL;
66666669

66676670
// If we have the closing ')', eat it.
66686671
Tracker.consumeClose();
@@ -6799,7 +6802,7 @@ bool Parser::ParseRefQualifier(bool &RefQualifierIsLValueRef,
67996802
/// Note that identifier-lists are only allowed for normal declarators, not for
68006803
/// abstract-declarators.
68016804
bool Parser::isFunctionDeclaratorIdentifierList() {
6802-
return !getLangOpts().CPlusPlus
6805+
return !getLangOpts().requiresStrictPrototypes()
68036806
&& Tok.is(tok::identifier)
68046807
&& !TryAltiVecVectorToken()
68056808
// K&R identifier lists can't have typedefs as identifiers, per C99
@@ -6833,6 +6836,10 @@ bool Parser::isFunctionDeclaratorIdentifierList() {
68336836
void Parser::ParseFunctionDeclaratorIdentifierList(
68346837
Declarator &D,
68356838
SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo) {
6839+
// We should never reach this point in C2x or C++.
6840+
assert(!getLangOpts().requiresStrictPrototypes() &&
6841+
"Cannot parse an identifier list in C2x or C++");
6842+
68366843
// If there was no identifier specified for the declarator, either we are in
68376844
// an abstract-declarator, or we are in a parameter declarator which was found
68386845
// to be abstract. In abstract-declarators, identifier lists are not valid:

clang/lib/Sema/SemaDecl.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8822,6 +8822,9 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D,
88228822
(D.getDeclSpec().isTypeRep() &&
88238823
D.getDeclSpec().getRepAsType().get()->isFunctionProtoType()) ||
88248824
(!R->getAsAdjusted<FunctionType>() && R->isFunctionProtoType());
8825+
assert(
8826+
(HasPrototype || !SemaRef.getLangOpts().requiresStrictPrototypes()) &&
8827+
"Strict prototypes are required");
88258828

88268829
NewFD = FunctionDecl::Create(
88278830
SemaRef.Context, DC, D.getBeginLoc(), NameInfo, R, TInfo, SC,

clang/lib/Sema/SemaType.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5270,8 +5270,11 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
52705270
FunctionType::ExtInfo EI(
52715271
getCCForDeclaratorChunk(S, D, DeclType.getAttrs(), FTI, chunkIndex));
52725272

5273-
if (!FTI.NumParams && !FTI.isVariadic && !LangOpts.CPlusPlus
5274-
&& !LangOpts.OpenCL) {
5273+
// OpenCL disallows functions without a prototype, but it doesn't enforce
5274+
// strict prototypes as in C2x because it allows a function definition to
5275+
// have an identifier list. See OpenCL 3.0 6.11/g for more details.
5276+
if (!FTI.NumParams && !FTI.isVariadic &&
5277+
!LangOpts.requiresStrictPrototypes() && !LangOpts.OpenCL) {
52755278
// Simple void foo(), where the incoming T is the result type.
52765279
T = Context.getFunctionNoProtoType(T, EI);
52775280
} else {
@@ -5290,8 +5293,10 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
52905293
S.Diag(FTI.Params[0].IdentLoc,
52915294
diag::err_ident_list_in_fn_declaration);
52925295
D.setInvalidType(true);
5293-
// Recover by creating a K&R-style function type.
5294-
T = Context.getFunctionNoProtoType(T, EI);
5296+
// Recover by creating a K&R-style function type, if possible.
5297+
T = (!LangOpts.requiresStrictPrototypes() && !LangOpts.OpenCL)
5298+
? Context.getFunctionNoProtoType(T, EI)
5299+
: Context.IntTy;
52955300
break;
52965301
}
52975302

clang/lib/StaticAnalyzer/Core/MemRegion.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,8 +1033,10 @@ const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D,
10331033
T = TSI->getType();
10341034
if (T.isNull())
10351035
T = getContext().VoidTy;
1036-
if (!T->getAs<FunctionType>())
1037-
T = getContext().getFunctionNoProtoType(T);
1036+
if (!T->getAs<FunctionType>()) {
1037+
FunctionProtoType::ExtProtoInfo Ext;
1038+
T = getContext().getFunctionType(T, None, Ext);
1039+
}
10381040
T = getContext().getBlockPointerType(T);
10391041

10401042
const BlockCodeRegion *BTR =

clang/test/Driver/no-knr-functions.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Ensure that we cannot disable strict prototypes, no matter how hard we try.
2+
3+
// RUN: not %clang -fno-no-knr-functions -x c++ %s 2>&1 | FileCheck --check-prefixes=NONO %s
4+
// RUN: not %clang -fno-no-knr-functions -x c %s 2>&1 | FileCheck --check-prefixes=NONO %s
5+
// RUN: not %clang -fno-no-knr-functions -std=c89 -x c %s 2>&1 | FileCheck --check-prefixes=NONO %s
6+
// RUN: not %clang -fknr-functions -x c++ %s 2>&1 | FileCheck --check-prefixes=POS %s
7+
// RUN: not %clang -fknr-functions -x c %s 2>&1 | FileCheck --check-prefixes=POS %s
8+
// RUN: not %clang -fknr-functions -std=c89 -x c %s 2>&1 | FileCheck --check-prefixes=POS %s
9+
10+
// NONO: error: unknown argument: '-fno-no-knr-functions'
11+
// POS: error: unknown argument: '-fknr-functions'

0 commit comments

Comments
 (0)