1
0
mirror of https://github.com/cc65/cc65.git synced 2024-12-23 04:30:10 +00:00

Added primitive support for the ISO C99 inline feature as well as the __inline__ extension.

No inlining is actually done but that part is not required by the standard.
This commit is contained in:
acqn 2024-01-14 00:08:41 +08:00
parent a173428fab
commit 0b06c34dfc
13 changed files with 184 additions and 12 deletions

View File

@ -121,7 +121,7 @@ static void Parse (void)
} }
/* Read the declaration specifier */ /* 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 */ /* Don't accept illegal storage classes */
if ((Spec.StorageClass & SC_STORAGEMASK) == SC_AUTO || 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)) { if ((Entry->Flags & SC_STORAGEMASK) == SC_STATIC && SymIsRef (Entry)) {
Warning ("Static function '%s' used but never defined", Warning ("Static function '%s' used but never defined",
Entry->Name); 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) static int ParseStorageClass (DeclSpec* Spec)
/* Parse storage class specifiers. Return true if a specifier is read even if /* 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 */ /* Check the storage class given */
unsigned StorageClass = ParseOneStorageClass (); 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) static void DuplicateQualifier (const char* Name)
/* Print an error message */ /* Print an error message */
{ {
@ -303,7 +356,8 @@ static void OptionalSpecifiers (DeclSpec* Spec, TypeCode* Qualifiers, typespec_t
*/ */
{ {
TypeCode Q = T_QUAL_NONE; TypeCode Q = T_QUAL_NONE;
int Continue; int HasStorageClass;
int HasFuncSpec;
do { do {
/* There may be type qualifiers *before* any storage class specifiers */ /* 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; *Qualifiers |= Q;
/* Parse storage class specifiers anyway then check */ /* Parse storage class specifiers anyway then check */
Continue = ParseStorageClass (Spec); HasStorageClass = ParseStorageClass (Spec);
if (Continue && (TSFlags & (TS_STORAGE_CLASS_SPEC | TS_FUNCTION_SPEC)) == 0) { if (HasStorageClass && (TSFlags & TS_STORAGE_CLASS_SPEC) == 0) {
Error ("Unexpected storage class specified"); 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 */ /* Parse attributes for this declarator */
ParseAttribute (D); 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 */ /* Check a few pre-C99 things */
if (D->Ident[0] != '\0' && (Spec->Flags & DS_TYPE_MASK) == DS_DEF_TYPE) { if (D->Ident[0] != '\0' && (Spec->Flags & DS_TYPE_MASK) == DS_DEF_TYPE) {
/* Check and warn about an implicit int return in the function */ /* 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; Spec->Flags &= ~DS_DEF_STORAGE;
/* Parse the type specifiers */ /* 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 no explicit storage class is given, use the default */
if ((Spec->StorageClass & SC_STORAGEMASK) == 0) { if ((Spec->StorageClass & SC_STORAGEMASK) == 0) {
@ -2495,7 +2563,9 @@ void CheckEmptyDecl (const DeclSpec* Spec)
** warning if not. ** 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 */ /* No declaration at all */
} else if ((Spec->Flags & DS_EXTRA_TYPE) == 0) { } else if ((Spec->Flags & DS_EXTRA_TYPE) == 0) {
Warning ("Declaration does not declare anything"); Warning ("Declaration does not declare anything");

View File

@ -1407,7 +1407,7 @@ static void Primary (ExprDesc* E)
} else { } else {
/* Let's see if this is a C99-style declaration */ /* Let's see if this is a C99-style declaration */
DeclSpec Spec; 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) { if ((Spec.Flags & DS_TYPE_MASK) != DS_NONE) {
Error ("Mixed declarations and code are not supported in cc65"); 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__"); 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 */ /* Check return type */
if (GetUnqualRawTypeCode (ReturnType) == T_INT) { if (GetUnqualRawTypeCode (ReturnType) == T_INT) {
/* Determine if this is a main function in a C99 environment that /* 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 */ /* 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 /* Check variable declarations. We need distinguish between a default
** int type and the end of variable declarations. So we will do the ** 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 */ /* Function specifiers */
TOK_INLINE, TOK_INLINE,
TOK_NORETURN,
TOK_FASTCALL, TOK_FASTCALL,
TOK_CDECL, TOK_CDECL,

View File

@ -151,6 +151,10 @@ struct CodeEntry;
#define SC_THREAD 0x08000000U /* UNSUPPORTED: Thread-local storage class */ #define SC_THREAD 0x08000000U /* UNSUPPORTED: Thread-local storage class */
#define SC_STORAGEMASK 0x0F000000U /* Storage type mask */ #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 */ /* 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.WrappedCall = WrappedCall;
Entry->V.F.WrappedCallData = WrappedCallData; 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 */ /* Add an alias of the global symbol to the local symbol table */
@ -1575,7 +1585,7 @@ void EmitExternals (void)
if (SymIsRef (Entry) && !SymIsDef (Entry)) { if (SymIsRef (Entry) && !SymIsDef (Entry)) {
/* An import */ /* An import */
g_defimport (Entry->Name, Flags & SC_ZEROPAGE); g_defimport (Entry->Name, Flags & SC_ZEROPAGE);
} else if (SymIsDef (Entry)) { } else if (SymIsDef (Entry) && ((Flags & SC_NOINLINEDEF) || (Flags & SC_INLINE) == 0)) {
/* An export */ /* An export */
g_defexport (Entry->Name, Flags & SC_ZEROPAGE); g_defexport (Entry->Name, Flags & SC_ZEROPAGE);
} }

View File

@ -63,6 +63,7 @@ CUSTOMSOURCES = \
# exact error output is required # exact error output is required
ERRORSOURCES = \ ERRORSOURCES = \
custom-reference-error.c \ custom-reference-error.c \
inline-error.c \
bug1889-missing-identifier.c \ bug1889-missing-identifier.c \
bug2312-preprocessor-error.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: Error: Identifier or ';' expected after declaration specifiers
bug1889-missing-identifier.c:3: Warning: Implicit 'int' is an obsolete feature 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 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;
}