Skip to content

Commit 16f7573

Browse files
Don't add void prototypes on functions that would otherwise not need to be rewritten (#688)
A function that does not otherwise need to be rewritten would sometimes be rewritten in order to replace a missing prototype with (void). This can trigger a rewriting error if the function is defined by a macro or in an unwritable file. After these changes, 3C still adds a prototype for functions with checked or itype return types, so int `*foo() { return 0; }` will still convert to `_Ptr<int> foo(void) { return 0; }` as is required by Checked C. Checked C also requires that a functions has a prototype for it to be called from inside a checked scope, but this is not handled by 3C; see issue #382. The status of this issue has not changed.
1 parent 54a80ab commit 16f7573

File tree

3 files changed

+50
-8
lines changed

3 files changed

+50
-8
lines changed

clang/lib/3C/DeclRewriter.cpp

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,8 @@ bool FunctionDeclBuilder::VisitFunctionDecl(FunctionDecl *FD) {
567567
bool RewriteGeneric = false;
568568

569569
bool DeclIsTypedef = false;
570-
if (TypeSourceInfo *TS = FD->getTypeSourceInfo()) {
570+
TypeSourceInfo *TS = FD->getTypeSourceInfo();
571+
if (TS != nullptr) {
571572
// This still could possibly be a typedef type if TS was NULL.
572573
// TypeSourceInfo is null for implicit function declarations, so if a
573574
// implicit declaration uses a typedef, it will be missed. That's fine
@@ -623,12 +624,10 @@ bool FunctionDeclBuilder::VisitFunctionDecl(FunctionDecl *FD) {
623624
RewriteParams = true;
624625
}
625626
} else {
626-
// No params and no param source: make explicit
627+
// Functions in CheckedC need prototypes, so replace empty parameter lists
628+
// with an explict (void). This updates the parameter list; the rewrite flag
629+
// will be set once it is known if the return needs to be rewritten.
627630
ParmStrs.push_back("void");
628-
QualType ReturnTy = FD->getReturnType();
629-
QualType Ty = FD->getTypeSourceInfo()->getType();
630-
if (!Ty->isFunctionProtoType() && ReturnTy->isPointerType())
631-
RewriteParams = true;
632631
}
633632

634633
// Get rewritten return variable.
@@ -661,6 +660,15 @@ bool FunctionDeclBuilder::VisitFunctionDecl(FunctionDecl *FD) {
661660
RewriteParams = true;
662661
}
663662

663+
// If this function was declared without a prototype, then we must add one
664+
// to be able to give it a checked return type. This was done by adding "void"
665+
// to the parameter list above. Here we indicate the parameter list should be
666+
// rewritten to include "void" only if the return is already being rewritten.
667+
// This avoids unnecessarily adding void to empty parameter lists on unchecked
668+
// functions.
669+
if (TS && !TS->getType()->isFunctionProtoType() && RewriteReturn)
670+
RewriteParams = true;
671+
664672
// If the function is declared using a typedef for the function type, then we
665673
// need to rewrite parameters and the return if either would have been
666674
// rewritten. What this does is expand the typedef to the full function type

clang/test/3C/dont_add_prototype.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: rm -rf %t*
2+
// RUN: 3c -base-dir=%S -alltypes -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_ALL","CHECK" %s
3+
// RUN: 3c -base-dir=%S -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_NOALL","CHECK" %s
4+
// RUN: 3c -base-dir=%S -alltypes -addcr %s -- | %clang -c -fcheckedc-extension -x c -o /dev/null -
5+
// RUN: 3c -base-dir=%S -alltypes -output-dir=%t.checked %s --
6+
// RUN: 3c -base-dir=%t.checked -alltypes %t.checked/dont_add_prototype.c -- | diff %t.checked/dont_add_prototype.c -
7+
8+
// Don't add a void prototype on functions if we don't need to. This can
9+
// potentially cause rewriting errors when attempting to rewrite functions
10+
// defined outside the basedir or defined by a macro.
11+
12+
// This function does not need to be rewritten. Its return type is unchecked
13+
// after solving. It should not be rewritten to use a void prototype in order
14+
// to avoid any potential rewritting issues.
15+
void *test0() { return 1; }
16+
//CHECK: void *test0() { return 1; }
17+
18+
// Trying to add a prototype in these examples caused a rewriting error
19+
// because the functions are defined by macros.
20+
21+
#define test_macro0 int *test_macro0()
22+
test_macro0 {
23+
//CHECK: test_macro0 {
24+
return 0;
25+
}
26+
27+
#define test_macro1 test_macro1()
28+
int *test_macro1 {
29+
//CHECK: int *test_macro1 {
30+
return 0;
31+
}
32+
33+
// Force conversion output.
34+
int *a;

clang/test/3C/generalize.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ void viewer_update(void *i) {
2222
void *getNull() { return 0; }
2323
// CHECK: _For_any(T) _Ptr<T> getNull(void) { return 0; }
2424
void *getOne() { return 1; }
25-
// CHECK: void *getOne(void) { return 1; }
25+
// CHECK: void *getOne() { return 1; }
2626
extern void *glob = 0;
2727
void *getGlobal() { return glob; }
28-
// CHECK: void *getGlobal(void) { return glob; }
28+
// CHECK: void *getGlobal() { return glob; }
2929

3030
// check type parameters
3131
void call_from_fn() {

0 commit comments

Comments
 (0)