mirror of
https://github.com/cc65/cc65.git
synced 2024-12-27 15:29:46 +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:
parent
a173428fab
commit
0b06c34dfc
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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");
|
||||||
|
@ -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");
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
|
||||||
|
@ -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 */
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
36
test/ref/inline-error.c
Normal 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 */
|
20
test/ref/inline-error.cref
Normal file
20
test/ref/inline-error.cref
Normal 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
20
test/val/inline-func.c
Normal 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;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user