mirror of
https://github.com/cc65/cc65.git
synced 2024-12-27 00:29:31 +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 */
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -76,6 +76,7 @@ typedef enum token_t {
|
||||
|
||||
/* Function specifiers */
|
||||
TOK_INLINE,
|
||||
TOK_NORETURN,
|
||||
TOK_FASTCALL,
|
||||
TOK_CDECL,
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
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