Merge pull request #2372 from acqn/C99Inline

[cc65] Added primitive support for the ISO C99 inline feature as well as the __inline__ extension
This commit is contained in:
Bob Andrews 2024-01-22 17:30:09 +01:00 committed by GitHub
commit deaf27d6a8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 184 additions and 12 deletions

View File

@ -121,7 +121,7 @@ static void Parse (void)
}
/* Read the declaration specifier */
ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_NONE);
ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT | TS_FUNCTION_SPEC, SC_NONE);
/* Don't accept illegal storage classes */
if ((Spec.StorageClass & SC_STORAGEMASK) == SC_AUTO ||
@ -560,6 +560,10 @@ void Compile (const char* FileName)
if ((Entry->Flags & SC_STORAGEMASK) == SC_STATIC && SymIsRef (Entry)) {
Warning ("Static function '%s' used but never defined",
Entry->Name);
} else if ((Entry->Flags & SC_INLINE) != 0) {
Warning ("Inline function '%s' %s but never defined",
Entry->Name,
SymIsRef (Entry) ? "used" : "declared");
}
}
}

View File

@ -124,9 +124,37 @@ static unsigned ParseOneStorageClass (void)
static unsigned ParseOneFuncSpec (void)
/* Parse and return a function specifier */
{
unsigned FuncSpec = 0;
/* Check the function specifier given */
switch (CurTok.Tok) {
case TOK_INLINE:
FuncSpec = SC_INLINE;
NextToken ();
break;
case TOK_NORETURN:
FuncSpec = SC_NORETURN;
NextToken ();
break;
default:
break;
}
return FuncSpec;
}
static int ParseStorageClass (DeclSpec* Spec)
/* Parse storage class specifiers. Return true if a specifier is read even if
** it was duplicated or disallowed. */
** it was duplicated or disallowed.
*/
{
/* Check the storage class given */
unsigned StorageClass = ParseOneStorageClass ();
@ -151,6 +179,31 @@ static int ParseStorageClass (DeclSpec* Spec)
static int ParseFuncSpecClass (DeclSpec* Spec)
/* Parse function specifiers. Return true if a specifier is read even if it
** was duplicated or disallowed.
*/
{
/* Check the function specifiers given */
unsigned FuncSpec = ParseOneFuncSpec ();
if (FuncSpec == 0) {
return 0;
}
while (FuncSpec != 0) {
if ((Spec->StorageClass & FuncSpec) != 0) {
Warning ("Duplicate function specifier");
}
Spec->StorageClass |= FuncSpec;
FuncSpec = ParseOneFuncSpec ();
}
return 1;
}
static void DuplicateQualifier (const char* Name)
/* Print an error message */
{
@ -303,7 +356,8 @@ static void OptionalSpecifiers (DeclSpec* Spec, TypeCode* Qualifiers, typespec_t
*/
{
TypeCode Q = T_QUAL_NONE;
int Continue;
int HasStorageClass;
int HasFuncSpec;
do {
/* There may be type qualifiers *before* any storage class specifiers */
@ -311,11 +365,17 @@ static void OptionalSpecifiers (DeclSpec* Spec, TypeCode* Qualifiers, typespec_t
*Qualifiers |= Q;
/* Parse storage class specifiers anyway then check */
Continue = ParseStorageClass (Spec);
if (Continue && (TSFlags & (TS_STORAGE_CLASS_SPEC | TS_FUNCTION_SPEC)) == 0) {
HasStorageClass = ParseStorageClass (Spec);
if (HasStorageClass && (TSFlags & TS_STORAGE_CLASS_SPEC) == 0) {
Error ("Unexpected storage class specified");
}
} while (Continue || Q != T_QUAL_NONE);
/* Parse function specifiers anyway then check */
HasFuncSpec = ParseFuncSpecClass (Spec);
if (HasFuncSpec && (TSFlags & TS_FUNCTION_SPEC) == 0) {
Error ("Unexpected function specifiers");
}
} while (Q != T_QUAL_NONE || HasStorageClass || HasFuncSpec);
}
@ -2375,6 +2435,14 @@ int ParseDecl (DeclSpec* Spec, Declarator* D, declmode_t Mode)
/* Parse attributes for this declarator */
ParseAttribute (D);
/* 'inline' is only allowed on functions */
if (Mode != DM_ACCEPT_PARAM_IDENT &&
(D->StorageClass & SC_TYPEMASK) != SC_FUNC &&
(D->StorageClass & SC_INLINE) == SC_INLINE) {
Error ("'inline' on non-function declaration");
D->StorageClass &= ~SC_INLINE;
}
/* Check a few pre-C99 things */
if (D->Ident[0] != '\0' && (Spec->Flags & DS_TYPE_MASK) == DS_DEF_TYPE) {
/* Check and warn about an implicit int return in the function */
@ -2478,7 +2546,7 @@ void ParseDeclSpec (DeclSpec* Spec, typespec_t TSFlags, unsigned DefStorage)
Spec->Flags &= ~DS_DEF_STORAGE;
/* Parse the type specifiers */
ParseTypeSpec (Spec, TSFlags | TS_STORAGE_CLASS_SPEC | TS_FUNCTION_SPEC);
ParseTypeSpec (Spec, TSFlags | TS_STORAGE_CLASS_SPEC);
/* If no explicit storage class is given, use the default */
if ((Spec->StorageClass & SC_STORAGEMASK) == 0) {
@ -2495,7 +2563,9 @@ void CheckEmptyDecl (const DeclSpec* Spec)
** warning if not.
*/
{
if ((Spec->Flags & DS_TYPE_MASK) == DS_NONE) {
if ((Spec->StorageClass & SC_INLINE) == SC_INLINE) {
Error ("'inline' on empty declaration");
} else if ((Spec->Flags & DS_TYPE_MASK) == DS_NONE) {
/* No declaration at all */
} else if ((Spec->Flags & DS_EXTRA_TYPE) == 0) {
Warning ("Declaration does not declare anything");

View File

@ -1407,7 +1407,7 @@ static void Primary (ExprDesc* E)
} else {
/* Let's see if this is a C99-style declaration */
DeclSpec Spec;
ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE, SC_AUTO);
ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_NONE | TS_FUNCTION_SPEC, SC_AUTO);
if ((Spec.Flags & DS_TYPE_MASK) != DS_NONE) {
Error ("Mixed declarations and code are not supported in cc65");

View File

@ -518,6 +518,12 @@ void NewFunc (SymEntry* Func, FuncDesc* D)
Error ("'main' cannot be declared as __fastcall__");
}
/* main() cannot be an inline function */
if ((Func->Flags & SC_INLINE) == SC_INLINE) {
Error ("'main' cannot be declared inline");
Func->Flags &= ~SC_INLINE;
}
/* Check return type */
if (GetUnqualRawTypeCode (ReturnType) == T_INT) {
/* Determine if this is a main function in a C99 environment that

View File

@ -563,7 +563,7 @@ void DeclareLocals (void)
}
/* Read the declaration specifier */
ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT, SC_AUTO);
ParseDeclSpec (&Spec, TS_DEFAULT_TYPE_INT | TS_FUNCTION_SPEC, SC_AUTO);
/* Check variable declarations. We need distinguish between a default
** int type and the end of variable declarations. So we will do the

View File

@ -76,6 +76,7 @@ typedef enum token_t {
/* Function specifiers */
TOK_INLINE,
TOK_NORETURN,
TOK_FASTCALL,
TOK_CDECL,

View File

@ -151,6 +151,10 @@ struct CodeEntry;
#define SC_THREAD 0x08000000U /* UNSUPPORTED: Thread-local storage class */
#define SC_STORAGEMASK 0x0F000000U /* Storage type mask */
/* Function specifiers */
#define SC_INLINE 0x10000000U /* Inline function */
#define SC_NORETURN 0x20000000U /* Noreturn function */
/* Label definition or reference */

View File

@ -1439,6 +1439,16 @@ SymEntry* AddGlobalSym (const char* Name, const Type* T, unsigned Flags)
Entry->V.F.WrappedCall = WrappedCall;
Entry->V.F.WrappedCallData = WrappedCallData;
}
/* A files cope function declaration with the 'extern' storage
** class or without the 'inline' specifier ensures that the
** function definition (if any) is a non-inline definition.
*/
if (SymTab == SymTab0 &&
((Flags & SC_STORAGEMASK) == SC_EXTERN ||
(Flags & SC_INLINE) == 0)) {
Entry->Flags |= SC_NOINLINEDEF;
}
}
/* Add an alias of the global symbol to the local symbol table */
@ -1575,7 +1585,7 @@ void EmitExternals (void)
if (SymIsRef (Entry) && !SymIsDef (Entry)) {
/* An import */
g_defimport (Entry->Name, Flags & SC_ZEROPAGE);
} else if (SymIsDef (Entry)) {
} else if (SymIsDef (Entry) && ((Flags & SC_NOINLINEDEF) || (Flags & SC_INLINE) == 0)) {
/* An export */
g_defexport (Entry->Name, Flags & SC_ZEROPAGE);
}

View File

@ -63,6 +63,7 @@ CUSTOMSOURCES = \
# exact error output is required
ERRORSOURCES = \
custom-reference-error.c \
inline-error.c \
bug1889-missing-identifier.c \
bug2312-preprocessor-error.c

View File

@ -1,4 +1,4 @@
bug1889-missing-identifier.c:3: Error: Identifier or ';' expected after declaration specifiers
bug1889-missing-identifier.c:3: Warning: Implicit 'int' is an obsolete feature
bug1889-missing-identifier.c:4: Error: Declaration specifier or identifier expected
bug1889-missing-identifier.c:4: Error: 'inline' on empty declaration
bug1889-missing-identifier.c:6: Error: Expression expected

36
test/ref/inline-error.c Normal file
View File

@ -0,0 +1,36 @@
/* C99 inline in declarations */
inline typedef int; /* Error */
static inline int; /* Error */
inline static int a1; /* Error */
int inline (*fp1)(void); /* Error */
typedef inline int f1_t(void); /* Error */
inline int f1a(void); /* OK here warning later */
inline extern int f1b(void); /* OK here warning later */
extern inline int f1b(void); /* Same as above */
inline static int f1c(void); /* OK here warning later */
static inline int f1c(void); /* Same as above */
void foo(inline int x); /* Error */
int a = sizeof (inline int); /* TODO: better error message */
int b = sizeof (inline int (int)); /* TODO: better error message */
inline int main(void) /* Error */
{
inline typedef int; /* Error */
static inline int; /* Error */
extern inline int a2; /* Error */
int inline (*fp2)(void); /* Error */
typedef inline int f2_t(void); /* Error */
inline int f2a(void); /* OK here warning later */
inline extern int f2b(void); /* OK here warning later */
extern inline int f2b(void); /* Same as above */
f1a(); /* Still imported */
f1b(); /* Still imported */
f1c(); /* Not imported */
f2a(); /* Still imported */
f2b(); /* Still imported */
}
/* Warning: non-external inline functions declared but undefined in TU */

View File

@ -0,0 +1,20 @@
inline-error.c:3: Error: 'inline' on empty declaration
inline-error.c:4: Error: 'inline' on empty declaration
inline-error.c:5: Error: 'inline' on non-function declaration
inline-error.c:6: Error: 'inline' on non-function declaration
inline-error.c:7: Error: 'inline' on non-function declaration
inline-error.c:14: Error: Unexpected function specifiers
inline-error.c:15: Error: Mixed declarations and code are not supported in cc65
inline-error.c:16: Error: Mixed declarations and code are not supported in cc65
inline-error.c:19: Error: 'main' cannot be declared inline
inline-error.c:20: Error: 'inline' on empty declaration
inline-error.c:21: Error: 'inline' on empty declaration
inline-error.c:22: Error: 'inline' on non-function declaration
inline-error.c:23: Error: 'inline' on non-function declaration
inline-error.c:24: Error: 'inline' on non-function declaration
inline-error.c:34: Warning: Variable 'fp2' is defined but never used
inline-error.c:37: Warning: Inline function 'f1a' used but never defined
inline-error.c:37: Warning: Inline function 'f1b' used but never defined
inline-error.c:37: Warning: Static function 'f1c' used but never defined
inline-error.c:37: Warning: Inline function 'f2a' used but never defined
inline-error.c:37: Warning: Inline function 'f2b' used but never defined

20
test/val/inline-func.c Normal file
View File

@ -0,0 +1,20 @@
/* C99 inline */
#include <stdlib.h>
inline static int f(int x, ...)
{
return x * 2;
}
extern inline int g(int x);
int main(void)
{
return f(g(7)) == 42 ? EXIT_SUCCESS : EXIT_FAILURE;
}
int g(int x)
{
return x * 3;
}