From 521f95824253ffcc367dd18177030a038039dd97 Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Tue, 22 Dec 2020 20:48:42 +0700 Subject: [PATCH 01/27] Allow a combination of `static` and `stock` specifiers in new-style function declarations --- source/compiler/sc1.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c index 43c5a7b0..1132940c 100644 --- a/source/compiler/sc1.c +++ b/source/compiler/sc1.c @@ -3748,7 +3748,7 @@ static void check_reparse(symbol *sym) static void funcstub(int fnative) { - int tok,tag,fpublic; + int tok,tag,fpublic,fstatic,fstock,fconst; char *str; cell val,size; char symbolname[sNAMEMAX+1]; @@ -3786,15 +3786,13 @@ static void funcstub(int fnative) dim[numdim++]=(int)size; } /* while */ + getclassspec(0,&fpublic,&fstatic,&fstock,&fconst); + /* a combination of 'public' and 'stock' is already checked in getclassspec() */ + if (fnative && (fpublic || fstatic || fstock) || (fpublic && fstock)) + error(42); /* invalid combination of class specifiers */ + else if (fconst) + error(10); /* illegal function or declaration */ tok=lex(&val,&str); - fpublic=(tok==tPUBLIC) || (tok==tSYMBOL && str[0]==PUBLIC_CHAR); - if (fnative) { - if (fpublic || tok==tSTOCK || tok==tSTATIC || (tok==tSYMBOL && *str==PUBLIC_CHAR)) - error(42); /* invalid combination of class specifiers */ - } else { - if (tok==tPUBLIC || tok==tSTOCK || tok==tSTATIC) - tok=lex(&val,&str); - } /* if */ if (tok==t__PRAGMA) { dopragma(); @@ -3811,6 +3809,12 @@ static void funcstub(int fnative) error(10); /* illegal function or declaration */ return; } /* if */ + if (str[0]==PUBLIC_CHAR) { + if (fnative) + error(42); /* invalid combination of class specifiers */ + else + fpublic=TRUE; + } /* if */ strcpy(symbolname,str); } /* if */ needtoken('('); /* only functions may be native/forward */ From 4a93ebd85beed97e11226869d68440af509ffc0f Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Wed, 23 Dec 2020 22:55:12 +0700 Subject: [PATCH 02/27] Forbid the use of `static` and `stock` specifiers on functions/variables whose name starts with `@` --- source/compiler/sc1.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c index 1132940c..e7f9d19a 100644 --- a/source/compiler/sc1.c +++ b/source/compiler/sc1.c @@ -2136,7 +2136,8 @@ static void declglb(char *firstname,int firsttag,int fpublic,int fstatic,int fst ispublic=fpublic; if (name[0]==PUBLIC_CHAR) { ispublic=TRUE; /* implicitly public variable */ - assert(!fstatic); + if (fstatic || fstock) + error(42); /* invalid combination of class specifiers */ } /* if */ while (matchtoken('[')) { ident=iARRAY; @@ -3810,9 +3811,9 @@ static void funcstub(int fnative) return; } /* if */ if (str[0]==PUBLIC_CHAR) { - if (fnative) + if (fnative || fstatic || fstock) error(42); /* invalid combination of class specifiers */ - else + if (!fnative) fpublic=TRUE; } /* if */ strcpy(symbolname,str); @@ -3967,7 +3968,7 @@ static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stoc funcline=fline; /* save line at which the function is defined */ if (symbolname[0]==PUBLIC_CHAR) { fpublic=TRUE; /* implicitly public function */ - if (stock) + if (stock || fstatic) error(42); /* invalid combination of class specifiers */ } /* if */ sym=fetchfunc(symbolname,tag);/* get a pointer to the function entry */ From a03a041b6ba6cbd866423a3d1843028fe0b15c54 Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Fri, 25 Dec 2020 19:06:21 +0700 Subject: [PATCH 03/27] Require specifiers `public`, `static` and `stock` in function definitions and repeated declarations if previous declaration(s) had those specifiers --- source/compiler/sc1.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c index e7f9d19a..708c9fdf 100644 --- a/source/compiler/sc1.c +++ b/source/compiler/sc1.c @@ -3758,6 +3758,7 @@ static void funcstub(int fnative) int numdim; symbol *sym,*sub; int opertok; + short filenum; unsigned int bck_attributes; char *bck_deprecate; /* in case the user tries to use __pragma("deprecated") * on a function argument */ @@ -3766,6 +3767,7 @@ static void funcstub(int fnative) lastst=0; litidx=0; /* clear the literal pool */ assert(loctab.next==NULL); /* local symbol table should be empty */ + filenum=fcurrent; /* save file number at the start of the declaration */ tag=pc_addtag(NULL); /* get the tag of the return value */ numdim=0; @@ -3826,8 +3828,16 @@ static void funcstub(int fnative) if (fnative) { sym->usage=(short)(uNATIVE | uRETVALUE | uDEFINE | (sym->usage & uPROTOTYPED)); sym->x.lib=curlibrary; - } else if (fpublic && opertok==0) { - sym->usage|=uPUBLIC; + } else { + if (((sym->usage & uPUBLIC)!=0 && !fpublic) || (sym->fnumber!=-1 && !fstatic) + || ((sym->usage & uSTOCK)!=0 && !fstock)) + error(25); /* function heading differs from prototype */ + if (fpublic && opertok==0) + sym->usage|=uPUBLIC; + if (fstatic) + sym->fnumber=filenum; + if (fstock) + sym->usage|=uSTOCK; } /* if */ sym->usage|=uFORWARD; check_reparse(sym); @@ -3974,10 +3984,15 @@ static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stoc sym=fetchfunc(symbolname,tag);/* get a pointer to the function entry */ if (sym==NULL || (sym->usage & uNATIVE)!=0) return TRUE; /* it was recognized as a function declaration, but not as a valid one */ + if (((sym->usage & uPUBLIC)!=0 && !fpublic) || (sym->fnumber!=-1 && !fstatic) + || ((sym->usage & uSTOCK)!=0 && !stock)) + error(25); /* function heading differs from prototype */ if (fpublic && opertok==0) sym->usage|=uPUBLIC; if (fstatic) sym->fnumber=filenum; + if (stock) + sym->usage|=uSTOCK; check_reparse(sym); /* we want public functions to be explicitly prototyped, as they are called * from the outside @@ -4041,8 +4056,6 @@ static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stoc } /* if */ begcseg(); sym->usage|=uDEFINE; /* set the definition flag */ - if (stock) - sym->usage|=uSTOCK; if (opertok!=0 && opererror) sym->usage &= ~uDEFINE; /* if the function has states, dump the label to the start of the function */ From 43b7431ce566fd6b95e777c8a8c9553849b12e29 Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Mon, 28 Dec 2020 00:37:59 +0700 Subject: [PATCH 04/27] Unset function class specifier flags at the end of compiler pass, so on the next pass "forward" declarations won't be required to have those specifiers at first --- source/compiler/sc.h | 5 +++++ source/compiler/sc1.c | 12 ++++++++---- source/compiler/sc2.c | 17 ++++++++++++----- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/source/compiler/sc.h b/source/compiler/sc.h index 94c89b19..a10e8e5e 100644 --- a/source/compiler/sc.h +++ b/source/compiler/sc.h @@ -229,6 +229,11 @@ typedef struct s_symbol { #define uRETNONE 0x010 /* uASSIGNED indicates that a value assigned to the variable is not used yet */ #define uASSIGNED 0x080 +/* function is declared/defined with the "static" specifier; this flag is being + * reset before the next compilation pass so "forward" declarations won't be + * required to have this specifier if the function was defined/re-declared with + * it only later */ +#define uSTATIC 0x200 /* uLOOPVAR is set when a variable is read inside of a loop condition. This is * used to detect situations when a variable is used in a loop condition, but * not modified inside of a loop body. */ diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c index 708c9fdf..b887414a 100644 --- a/source/compiler/sc1.c +++ b/source/compiler/sc1.c @@ -3829,13 +3829,15 @@ static void funcstub(int fnative) sym->usage=(short)(uNATIVE | uRETVALUE | uDEFINE | (sym->usage & uPROTOTYPED)); sym->x.lib=curlibrary; } else { - if (((sym->usage & uPUBLIC)!=0 && !fpublic) || (sym->fnumber!=-1 && !fstatic) + if (((sym->usage & uPUBLIC)!=0 && !fpublic) || ((sym->usage & uSTATIC)!=0 && !fstatic) || ((sym->usage & uSTOCK)!=0 && !fstock)) error(25); /* function heading differs from prototype */ if (fpublic && opertok==0) sym->usage|=uPUBLIC; - if (fstatic) + if (fstatic) { + sym->usage |= uSTATIC; sym->fnumber=filenum; + } /* if */ if (fstock) sym->usage|=uSTOCK; } /* if */ @@ -3984,13 +3986,15 @@ static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stoc sym=fetchfunc(symbolname,tag);/* get a pointer to the function entry */ if (sym==NULL || (sym->usage & uNATIVE)!=0) return TRUE; /* it was recognized as a function declaration, but not as a valid one */ - if (((sym->usage & uPUBLIC)!=0 && !fpublic) || (sym->fnumber!=-1 && !fstatic) + if (((sym->usage & uPUBLIC)!=0 && !fpublic) || ((sym->usage & uSTATIC)!=0 && !fstatic) || ((sym->usage & uSTOCK)!=0 && !stock)) error(25); /* function heading differs from prototype */ if (fpublic && opertok==0) sym->usage|=uPUBLIC; - if (fstatic) + if (fstatic) { + sym->usage |= uSTATIC; sym->fnumber=filenum; + } /* if */ if (stock) sym->usage|=uSTOCK; check_reparse(sym); diff --git a/source/compiler/sc2.c b/source/compiler/sc2.c index 7a7b9771..a3ec88bf 100644 --- a/source/compiler/sc2.c +++ b/source/compiler/sc2.c @@ -3155,11 +3155,18 @@ SC_FUNC void delete_symbols(symbol *root,int level,int delete_labels,int delete_ if (sym->states!=NULL) for (stateptr=sym->states->first; stateptr!=NULL; stateptr=stateptr->next) stateptr->value=0; - /* for user defined operators, also remove the "prototyped" flag, as - * user-defined operators *must* be declared before use - */ - if (sym->ident==iFUNCTN && !alpha(*sym->name)) - sym->usage &= ~uPROTOTYPED; + if (sym->ident==iFUNCTN) { + /* for user defined operators, also remove the "prototyped" flag, as + * user-defined operators *must* be declared before use + */ + if (!alpha(*sym->name)) + sym->usage &= ~uPROTOTYPED; + /* unset the class specifier flags, so on the next compilation pass + * the declaration(s) for this function won't be required to have all + * specifiers the function has been defined with on the previous pass + */ + sym->usage &= ~(uPUBLIC | uSTATIC | uSTOCK); + } /* if */ root=sym; /* skip the symbol */ } /* if */ } /* while */ From a4a55f09427d29f8b42784935df3ccc47895a103 Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Mon, 28 Dec 2020 01:02:40 +0700 Subject: [PATCH 05/27] Don't accept new class specifiers in "forward" re-declarations if the function has already been defined --- source/compiler/sc1.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c index b887414a..7e92464b 100644 --- a/source/compiler/sc1.c +++ b/source/compiler/sc1.c @@ -3832,14 +3832,22 @@ static void funcstub(int fnative) if (((sym->usage & uPUBLIC)!=0 && !fpublic) || ((sym->usage & uSTATIC)!=0 && !fstatic) || ((sym->usage & uSTOCK)!=0 && !fstock)) error(25); /* function heading differs from prototype */ - if (fpublic && opertok==0) - sym->usage|=uPUBLIC; - if (fstatic) { - sym->usage |= uSTATIC; - sym->fnumber=filenum; + if ((sym->usage & uDEFINE)!=0) { + /* if the function has already been defined ("finalized"), we can't accept + * any new class specifiers */ + if ((fpublic && (sym->usage & uPUBLIC)==0) || (fstatic && (sym->usage & uSTATIC)==0) + || (fstock && (sym->usage & uSTOCK)==0)) + error(25); /* function heading differs from prototype */ + } else { + if (fpublic && opertok==0) + sym->usage|=uPUBLIC; + if (fstatic) { + sym->usage |= uSTATIC; + sym->fnumber=filenum; + } /* if */ + if (fstock) + sym->usage|=uSTOCK; } /* if */ - if (fstock) - sym->usage|=uSTOCK; } /* if */ sym->usage|=uFORWARD; check_reparse(sym); From bb707e854e19df6fa3e24d98fa66d7592d83324b Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Tue, 5 Jan 2021 17:53:05 +0700 Subject: [PATCH 06/27] Add a separate flag to memoize the use of "public" specifier in function declarations/definitions. Also rename "uSTATIC" into "uDECLSTATIC". --- source/compiler/sc.h | 10 +++++++++- source/compiler/sc1.c | 14 +++++++------- source/compiler/sc2.c | 2 +- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/source/compiler/sc.h b/source/compiler/sc.h index a10e8e5e..758a321c 100644 --- a/source/compiler/sc.h +++ b/source/compiler/sc.h @@ -233,7 +233,15 @@ typedef struct s_symbol { * reset before the next compilation pass so "forward" declarations won't be * required to have this specifier if the function was defined/re-declared with * it only later */ -#define uSTATIC 0x200 +#define uDECLSTATIC 0x200 + /* function is declared/defined with the "public" specifier; this flag is + * similar to "uPUBLIC", except that it's being reset before the next + * compilation pass so "forward" declarations won't be required to have this + * specifier if the function was defined/re-declared with it only later + * (the original "uPUBLIC" flag can't be reset at the end of compilation pass, + * as later it's used for generation of the table of public functions and the + * stubs for state functions) */ +#define uDECLPUBLIC 0x800 /* uLOOPVAR is set when a variable is read inside of a loop condition. This is * used to detect situations when a variable is used in a loop condition, but * not modified inside of a loop body. */ diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c index 7e92464b..9b232c5c 100644 --- a/source/compiler/sc1.c +++ b/source/compiler/sc1.c @@ -3829,20 +3829,20 @@ static void funcstub(int fnative) sym->usage=(short)(uNATIVE | uRETVALUE | uDEFINE | (sym->usage & uPROTOTYPED)); sym->x.lib=curlibrary; } else { - if (((sym->usage & uPUBLIC)!=0 && !fpublic) || ((sym->usage & uSTATIC)!=0 && !fstatic) + if (((sym->usage & uDECLPUBLIC)!=0 && !fpublic) || ((sym->usage & uDECLSTATIC)!=0 && !fstatic) || ((sym->usage & uSTOCK)!=0 && !fstock)) error(25); /* function heading differs from prototype */ if ((sym->usage & uDEFINE)!=0) { /* if the function has already been defined ("finalized"), we can't accept * any new class specifiers */ - if ((fpublic && (sym->usage & uPUBLIC)==0) || (fstatic && (sym->usage & uSTATIC)==0) + if ((fpublic && (sym->usage & uDECLPUBLIC)==0) || (fstatic && (sym->usage & uDECLSTATIC)==0) || (fstock && (sym->usage & uSTOCK)==0)) error(25); /* function heading differs from prototype */ } else { if (fpublic && opertok==0) - sym->usage|=uPUBLIC; + sym->usage |= (uPUBLIC | uDECLPUBLIC); if (fstatic) { - sym->usage |= uSTATIC; + sym->usage |= uDECLSTATIC; sym->fnumber=filenum; } /* if */ if (fstock) @@ -3994,13 +3994,13 @@ static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stoc sym=fetchfunc(symbolname,tag);/* get a pointer to the function entry */ if (sym==NULL || (sym->usage & uNATIVE)!=0) return TRUE; /* it was recognized as a function declaration, but not as a valid one */ - if (((sym->usage & uPUBLIC)!=0 && !fpublic) || ((sym->usage & uSTATIC)!=0 && !fstatic) + if (((sym->usage & uDECLPUBLIC)!=0 && !fpublic) || ((sym->usage & uDECLSTATIC)!=0 && !fstatic) || ((sym->usage & uSTOCK)!=0 && !stock)) error(25); /* function heading differs from prototype */ if (fpublic && opertok==0) - sym->usage|=uPUBLIC; + sym->usage |= (uPUBLIC | uDECLPUBLIC); if (fstatic) { - sym->usage |= uSTATIC; + sym->usage |= uDECLSTATIC; sym->fnumber=filenum; } /* if */ if (stock) diff --git a/source/compiler/sc2.c b/source/compiler/sc2.c index a3ec88bf..54ba11a5 100644 --- a/source/compiler/sc2.c +++ b/source/compiler/sc2.c @@ -3165,7 +3165,7 @@ SC_FUNC void delete_symbols(symbol *root,int level,int delete_labels,int delete_ * the declaration(s) for this function won't be required to have all * specifiers the function has been defined with on the previous pass */ - sym->usage &= ~(uPUBLIC | uSTATIC | uSTOCK); + sym->usage &= ~(uDECLPUBLIC | uDECLSTATIC | uSTOCK); } /* if */ root=sym; /* skip the symbol */ } /* if */ From 9658c873926b85a198fe73c29b007ee720c1ec6b Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Thu, 7 Jan 2021 17:34:29 +0700 Subject: [PATCH 07/27] Add tests --- .../compiler/tests/class_specifiers_p1.meta | 29 +++++++++ source/compiler/tests/class_specifiers_p1.pwn | 61 +++++++++++++++++++ .../compiler/tests/class_specifiers_p2.meta | 7 +++ source/compiler/tests/class_specifiers_p2.pwn | 9 +++ 4 files changed, 106 insertions(+) create mode 100644 source/compiler/tests/class_specifiers_p1.meta create mode 100644 source/compiler/tests/class_specifiers_p1.pwn create mode 100644 source/compiler/tests/class_specifiers_p2.meta create mode 100644 source/compiler/tests/class_specifiers_p2.pwn diff --git a/source/compiler/tests/class_specifiers_p1.meta b/source/compiler/tests/class_specifiers_p1.meta new file mode 100644 index 00000000..204fdbd1 --- /dev/null +++ b/source/compiler/tests/class_specifiers_p1.meta @@ -0,0 +1,29 @@ +{ + 'test_type': 'output_check', + 'errors': """ +class_specifiers_p1.pwn(22) : error 010: invalid function or declaration +class_specifiers_p1.pwn(23) : error 010: invalid function or declaration +class_specifiers_p1.pwn(24) : error 010: invalid function or declaration +class_specifiers_p1.pwn(27) : error 042: invalid combination of class specifiers +class_specifiers_p1.pwn(28) : error 042: invalid combination of class specifiers +class_specifiers_p1.pwn(29) : error 042: invalid combination of class specifiers +class_specifiers_p1.pwn(30) : error 042: invalid combination of class specifiers +class_specifiers_p1.pwn(31) : error 042: invalid combination of class specifiers +class_specifiers_p1.pwn(32) : error 042: invalid combination of class specifiers +class_specifiers_p1.pwn(33) : error 001: expected token: ";", but found "(" +class_specifiers_p1.pwn(34) : error 001: expected token: ";", but found "(" +class_specifiers_p1.pwn(37) : error 042: invalid combination of class specifiers +class_specifiers_p1.pwn(38) : error 042: invalid combination of class specifiers +class_specifiers_p1.pwn(39) : error 042: invalid combination of class specifiers +class_specifiers_p1.pwn(40) : error 042: invalid combination of class specifiers +class_specifiers_p1.pwn(41) : error 042: invalid combination of class specifiers +class_specifiers_p1.pwn(42) : error 042: invalid combination of class specifiers +class_specifiers_p1.pwn(53) : error 042: invalid combination of class specifiers +class_specifiers_p1.pwn(54) : error 042: invalid combination of class specifiers +class_specifiers_p1.pwn(55) : error 042: invalid combination of class specifiers +class_specifiers_p1.pwn(56) : error 042: invalid combination of class specifiers +class_specifiers_p1.pwn(57) : error 042: invalid combination of class specifiers +class_specifiers_p1.pwn(58) : error 042: invalid combination of class specifiers +class_specifiers_p1.pwn(59) : error 042: invalid combination of class specifiers +""" +} diff --git a/source/compiler/tests/class_specifiers_p1.pwn b/source/compiler/tests/class_specifiers_p1.pwn new file mode 100644 index 00000000..a8223746 --- /dev/null +++ b/source/compiler/tests/class_specifiers_p1.pwn @@ -0,0 +1,61 @@ +#pragma warning disable 218 // old style prototypes used with optional semicolons + +forward stock Func1(); +stock Func1(); +stock Func1(){} + +forward public Func2(); +public Func2(); +public Func2(){} + +forward Func3() __pragma("unused"); +Func3(); +Func3(){} + +forward static Func4() __pragma("unused"); +static Func4(); +static Func4(){} + +native NativeFunc(); + +// `const` is illegal in forward declarations. +forward const ConstFunc1(); // error 010: invalid function or declaration +forward static const ConstFunc2(); // error 010: invalid function or declaration +native const NativeConstFunc1(); // error 010: invalid function or declaration + +// Combinations of `static`+`public` and `stock`+`public` are illegal. +forward static public StaticPublicFunc1(); // error 042: invalid combination of class specifiers +forward public static StaticPublicFunc2(); // error 042: invalid combination of class specifiers +static public StaticPublicFunc1(); // error 042: invalid combination of class specifiers +public static StaticPublicFunc2(); // error 042: invalid combination of class specifiers +forward stock public StockPublicFunc1(); // error 042: invalid combination of class specifiers +forward public stock StockPublicFunc2(); // error 042: invalid combination of class specifiers +stock public StockPublicFunc1(); // error 001: expected token: ";", but found "(" +public stock StockPublicFunc2(); // error 001: expected token: ";", but found "(" + +// None of the other class specifiers can be used in combination with `native`. +native stock NativeFunc2(); // error 042: invalid combination of class specifiers +native public NativeFunc3(); // error 042: invalid combination of class specifiers +native static NativeFunc4(); // error 042: invalid combination of class specifiers +stock native NativeFunc5(); // error 010: invalid function or declaration +public native NativeFunc5(); // error 010: invalid function or declaration +static native NativeFunc5(); // error 010: invalid function or declaration + +// Names starting with '@' imply `public`, which is correct when used +// in combination with an actual "public" specifier or without any specifiers +// at all, but shouldn't work with `static` and `native`. +forward @Func1(); +@Func1(); +@Func1(){} +forward public @Func2(); +public @Func2(); +public @Func2(){} +forward stock @Func3(); // error 042: invalid combination of class specifiers +stock @Func3(); // error 042: invalid combination of class specifiers +stock @Func3(){} // error 042: invalid combination of class specifiers +forward static @Func4(); // error 042: invalid combination of class specifiers +static @Func4(); // error 042: invalid combination of class specifiers +static @Func4(){} // error 042: invalid combination of class specifiers +native @NativeFunc(); // error 042: invalid combination of class specifiers + +main(){} diff --git a/source/compiler/tests/class_specifiers_p2.meta b/source/compiler/tests/class_specifiers_p2.meta new file mode 100644 index 00000000..e94f7b80 --- /dev/null +++ b/source/compiler/tests/class_specifiers_p2.meta @@ -0,0 +1,7 @@ +{ + 'test_type': 'output_check', + 'errors': """ +class_specifiers_p2.pwn(4) : error 042: invalid combination of class specifiers +class_specifiers_p2.pwn(7) : error 042: invalid combination of class specifiers +""" +} diff --git a/source/compiler/tests/class_specifiers_p2.pwn b/source/compiler/tests/class_specifiers_p2.pwn new file mode 100644 index 00000000..9db83d12 --- /dev/null +++ b/source/compiler/tests/class_specifiers_p2.pwn @@ -0,0 +1,9 @@ +#pragma warning disable 218 // old style prototypes used with optional semicolons + +// This should cause an error, as public variables can't be stock +stock @var1 = 0; // error 042: invalid combination of class specifiers + +// This shouldn't cause an assertion failure +static @var2 = 0; // error 042: invalid combination of class specifiers + +main(){} From 76a4cb246b374638cc81a9789858f83b32099aa9 Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Thu, 7 Jan 2021 18:54:55 +0700 Subject: [PATCH 08/27] Rename the "stock" argument into "fstock" to better match the naming of other arguments --- source/compiler/sc1.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c index 9b232c5c..27137b14 100644 --- a/source/compiler/sc1.c +++ b/source/compiler/sc1.c @@ -108,7 +108,7 @@ static cell init(int ident,int *tag,int *errorfound); static int getstates(const char *funcname); static void attachstatelist(symbol *sym, int state_id); static void funcstub(int fnative); -static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stock); +static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int fstock); static int declargs(symbol *sym,int chkshadow); static void doarg(char *name,int ident,int offset,int tags[],int numtags, int fpublic,int fconst,int written,int chkshadow,arginfo *arg); @@ -3931,7 +3931,7 @@ static void funcstub(int fnative) * glb_declared (altered) * sc_alignnext (altered) */ -static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stock) +static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int fstock) { symbol *sym,*lvar,*depend; int argcnt,tok,tag,funcline,i; @@ -3961,7 +3961,7 @@ static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stoc } else { tag= (firsttag>=0) ? firsttag : pc_addtag(NULL); tok=lex(&val,&str); - if (tok==tNATIVE || (tok==tPUBLIC && stock)) + if (tok==tNATIVE || (tok==tPUBLIC && fstock)) error(42); /* invalid combination of class specifiers */ if (tok==t__PRAGMA) { dopragma(); @@ -3988,14 +3988,14 @@ static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stoc funcline=fline; /* save line at which the function is defined */ if (symbolname[0]==PUBLIC_CHAR) { fpublic=TRUE; /* implicitly public function */ - if (stock || fstatic) + if (fstock || fstatic) error(42); /* invalid combination of class specifiers */ } /* if */ sym=fetchfunc(symbolname,tag);/* get a pointer to the function entry */ if (sym==NULL || (sym->usage & uNATIVE)!=0) return TRUE; /* it was recognized as a function declaration, but not as a valid one */ if (((sym->usage & uDECLPUBLIC)!=0 && !fpublic) || ((sym->usage & uDECLSTATIC)!=0 && !fstatic) - || ((sym->usage & uSTOCK)!=0 && !stock)) + || ((sym->usage & uSTOCK)!=0 && !fstock)) error(25); /* function heading differs from prototype */ if (fpublic && opertok==0) sym->usage |= (uPUBLIC | uDECLPUBLIC); @@ -4003,7 +4003,7 @@ static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stoc sym->usage |= uDECLSTATIC; sym->fnumber=filenum; } /* if */ - if (stock) + if (fstock) sym->usage|=uSTOCK; check_reparse(sym); /* we want public functions to be explicitly prototyped, as they are called From 60222eb8a0f61ed593d457140709808b7520c2b3 Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Thu, 11 Feb 2021 20:02:15 +0700 Subject: [PATCH 09/27] Simplify a few lines of code in `funcstub()` --- source/compiler/sc1.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c index 27137b14..0ffa9628 100644 --- a/source/compiler/sc1.c +++ b/source/compiler/sc1.c @@ -3795,13 +3795,11 @@ static void funcstub(int fnative) error(42); /* invalid combination of class specifiers */ else if (fconst) error(10); /* illegal function or declaration */ - tok=lex(&val,&str); - if (tok==t__PRAGMA) { + if (matchtoken(t__PRAGMA)) dopragma(); - tok=lex(&val,&str); - } /* if */ - + + tok=lex(&val,&str); if (tok==tOPERATOR) { opertok=operatorname(symbolname); if (opertok==0) From 95f017abba0a0a97d9972f73607ee2de3d91ef4f Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Thu, 11 Feb 2021 21:06:38 +0700 Subject: [PATCH 10/27] funcstub(): Expect the function tag and the return array size after the class specifiers and `__pragma` --- source/compiler/sc1.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c index 0ffa9628..1152fab3 100644 --- a/source/compiler/sc1.c +++ b/source/compiler/sc1.c @@ -3769,6 +3769,16 @@ static void funcstub(int fnative) assert(loctab.next==NULL); /* local symbol table should be empty */ filenum=fcurrent; /* save file number at the start of the declaration */ + getclassspec(0,&fpublic,&fstatic,&fstock,&fconst); + /* a combination of 'public' and 'stock' is already checked in getclassspec() */ + if (fnative && (fpublic || fstatic || fstock) || (fpublic && fstock)) + error(42); /* invalid combination of class specifiers */ + else if (fconst) + error(10); /* illegal function or declaration */ + + if (matchtoken(t__PRAGMA)) + dopragma(); + tag=pc_addtag(NULL); /* get the tag of the return value */ numdim=0; while (matchtoken('[')) { @@ -3788,16 +3798,6 @@ static void funcstub(int fnative) #endif dim[numdim++]=(int)size; } /* while */ - - getclassspec(0,&fpublic,&fstatic,&fstock,&fconst); - /* a combination of 'public' and 'stock' is already checked in getclassspec() */ - if (fnative && (fpublic || fstatic || fstock) || (fpublic && fstock)) - error(42); /* invalid combination of class specifiers */ - else if (fconst) - error(10); /* illegal function or declaration */ - - if (matchtoken(t__PRAGMA)) - dopragma(); tok=lex(&val,&str); if (tok==tOPERATOR) { From 65f4b303ba83e0047c085b2496d1ded1b1f54137 Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Sat, 13 Feb 2021 21:58:00 +0700 Subject: [PATCH 11/27] Update tests --- source/compiler/tests/class_specifiers_p2.meta | 1 + source/compiler/tests/class_specifiers_p2.pwn | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/source/compiler/tests/class_specifiers_p2.meta b/source/compiler/tests/class_specifiers_p2.meta index e94f7b80..edafd2b3 100644 --- a/source/compiler/tests/class_specifiers_p2.meta +++ b/source/compiler/tests/class_specifiers_p2.meta @@ -3,5 +3,6 @@ 'errors': """ class_specifiers_p2.pwn(4) : error 042: invalid combination of class specifiers class_specifiers_p2.pwn(7) : error 042: invalid combination of class specifiers +class_specifiers_p2.pwn(11) : error 010: invalid function or declaration """ } diff --git a/source/compiler/tests/class_specifiers_p2.pwn b/source/compiler/tests/class_specifiers_p2.pwn index 9db83d12..a85610e4 100644 --- a/source/compiler/tests/class_specifiers_p2.pwn +++ b/source/compiler/tests/class_specifiers_p2.pwn @@ -6,4 +6,9 @@ stock @var1 = 0; // error 042: invalid combination of class specifiers // This shouldn't cause an assertion failure static @var2 = 0; // error 042: invalid combination of class specifiers +// The compiler should expect the function tag and the return array size AFTER +// the class specifiers and `__pragma` +forward Tag:[2] static stock Func(); // error 010: invalid function or declaration +forward static stock Tag:[2] Func2(); // OK + main(){} From 1e9418d88b8a48443cdc224be157f47938a9aaa4 Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Sat, 13 Feb 2021 22:40:50 +0700 Subject: [PATCH 12/27] Move the code for handling operator `__pragma` out of `newfunc()` This fixes a bug with `__pragma` being expected twice in certain cases. --- source/compiler/sc1.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c index 1152fab3..7cc6ee52 100644 --- a/source/compiler/sc1.c +++ b/source/compiler/sc1.c @@ -1842,7 +1842,10 @@ static void parse(void) case tLABEL: case tSYMBOL: case tOPERATOR: - lexpush(); + if (tok==t__PRAGMA) + dopragma(); + else + lexpush(); if (!newfunc(NULL,-1,FALSE,FALSE,FALSE)) { error(10); /* illegal function or declaration */ lexclr(TRUE); /* drop the rest of the line */ @@ -3961,10 +3964,6 @@ static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int fsto tok=lex(&val,&str); if (tok==tNATIVE || (tok==tPUBLIC && fstock)) error(42); /* invalid combination of class specifiers */ - if (tok==t__PRAGMA) { - dopragma(); - tok=lex(&val,&str); - } /* if */ if (tok==tOPERATOR) { opertok=operatorname(symbolname); if (opertok==0) From 0e345d15451ee2a604fc6c64eaaa0d790f7cd856 Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Sat, 13 Feb 2021 22:42:13 +0700 Subject: [PATCH 13/27] Expect `__pragma` before the tag in function/variable declarations --- source/compiler/sc1.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c index 7cc6ee52..844b940e 100644 --- a/source/compiler/sc1.c +++ b/source/compiler/sc1.c @@ -2047,6 +2047,9 @@ static void declfuncvar(int fpublic,int fstatic,int fstock,int fconst) cell val; int invalidfunc; + if (matchtoken(t__PRAGMA)) + dopragma(); + tag=pc_addtag(NULL); tok=lex(&val,&str); /* if we arrived here, this may not be a declaration of a native function @@ -2057,11 +2060,6 @@ static void declfuncvar(int fpublic,int fstatic,int fstock,int fconst) return; } /* if */ - if (tok==t__PRAGMA) { - dopragma(); - tok=lex(&val,&str); - } /* if */ - if (tok!=tSYMBOL && tok!=tOPERATOR) { lexpush(); needtoken(tSYMBOL); From 740b6de9352a28ccd993a3e16724faedd3c28632 Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Sat, 13 Feb 2021 23:52:50 +0700 Subject: [PATCH 14/27] Move the code for handling `__pragma` in prefix form out of `newfunc()` This fixes another bug with `__pragma` being expected twice in variable declarations starting with specifiers `public`, `static` and `stock`. --- source/compiler/sc1.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c index 844b940e..a41e13f9 100644 --- a/source/compiler/sc1.c +++ b/source/compiler/sc1.c @@ -1795,8 +1795,11 @@ static void parse(void) emit_flags &= ~efGLOBAL; break; case tNEW: - if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst)) + if (getclassspec(tok,&fpublic,&fstatic,&fstock,&fconst)) { + if (matchtoken(t__PRAGMA)) + dopragma(); declglb(NULL,0,fpublic,fstatic,fstock,fconst); + } /* if */ break; case tSTATIC: if (matchtoken(tENUM)) { @@ -2127,8 +2130,6 @@ static void declglb(char *firstname,int firsttag,int fpublic,int fstatic,int fst firstname=NULL; } else { tag=pc_addtag(NULL); - if (matchtoken(t__PRAGMA)) - dopragma(); if (lex(&val,&str)!=tSYMBOL) /* read in (new) token */ error_suggest(20,str,NULL,estSYMBOL,esfFUNCTION); /* invalid symbol name */ assert(strlen(str)<=sNAMEMAX); From 91e3d42714948aa12c2cfb3d149a94f6e8a93a77 Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Sat, 13 Feb 2021 23:53:25 +0700 Subject: [PATCH 15/27] Update tests --- source/compiler/tests/__pragma.meta | 26 ++++++++++++++------------ source/compiler/tests/__pragma.pwn | 11 +++++++++++ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/source/compiler/tests/__pragma.meta b/source/compiler/tests/__pragma.meta index e3176562..b6ffcc7d 100644 --- a/source/compiler/tests/__pragma.meta +++ b/source/compiler/tests/__pragma.meta @@ -3,17 +3,19 @@ 'errors': """ __pragma.pwn(6) : error 001: expected token: "-identifier-", but found "const" __pragma.pwn(10) : error 001: expected token: "-identifier-", but found "__pragma" -__pragma.pwn(36) : warning 234: function is deprecated (symbol "Func") - use OtherFunc() instead -__pragma.pwn(40) : warning 234: function is deprecated (symbol "NakedFunc") - use NakedFunc2() instead -__pragma.pwn(43) : warning 207: unknown #pragma -__pragma.pwn(44) : warning 207: unknown #pragma -__pragma.pwn(48) : warning 207: unknown #pragma -__pragma.pwn(62) : warning 200: symbol "long_name2_zzzzzzzz_zzzzzzzzz_z" is truncated to 31 characters -__pragma.pwn(66) : warning 207: unknown #pragma -__pragma.pwn(67) : warning 207: unknown #pragma -__pragma.pwn(68) : warning 207: unknown #pragma -__pragma.pwn(33) : warning 203: symbol is never used: "f" -__pragma.pwn(29) : warning 204: symbol is assigned a value that is never used: "e" -__pragma.pwn(70) : warning 203: symbol is never used: "operator~(Tag:)" +__pragma.pwn(22) : error 001: expected token: "-identifier-", but found "__pragma" +__pragma.pwn(23) : error 001: expected token: "-identifier-", but found "__pragma" +__pragma.pwn(47) : warning 234: function is deprecated (symbol "Func") - use OtherFunc() instead +__pragma.pwn(51) : warning 234: function is deprecated (symbol "NakedFunc") - use NakedFunc2() instead +__pragma.pwn(54) : warning 207: unknown #pragma +__pragma.pwn(55) : warning 207: unknown #pragma +__pragma.pwn(59) : warning 207: unknown #pragma +__pragma.pwn(73) : warning 200: symbol "long_name2_zzzzzzzz_zzzzzzzzz_z" is truncated to 31 characters +__pragma.pwn(77) : warning 207: unknown #pragma +__pragma.pwn(78) : warning 207: unknown #pragma +__pragma.pwn(79) : warning 207: unknown #pragma +__pragma.pwn(44) : warning 203: symbol is never used: "f" +__pragma.pwn(40) : warning 204: symbol is assigned a value that is never used: "e" +__pragma.pwn(81) : warning 203: symbol is never used: "operator~(Tag:)" """ } diff --git a/source/compiler/tests/__pragma.pwn b/source/compiler/tests/__pragma.pwn index 91610e61..12bdc266 100644 --- a/source/compiler/tests/__pragma.pwn +++ b/source/compiler/tests/__pragma.pwn @@ -9,8 +9,19 @@ stock Func3(__pragma("unread") const arg) {} // "__pragma" can't be used between the tag and the symbol name stock Func4(Tag: __pragma("unread") arg) {} +// The compiler should expect "__pragma" before the tag +forward stock __pragma("unused") Tag:Func5(); +stock __pragma("unused") Tag:Func5(){} +__pragma("unused") Tag:Func6(){} +stock __pragma("unused") Tag:global_var1 = 0; + operator~(Tag:val[],count) {} +// The compiler shouldn't expect "__pragma" in prefix form twice in declarations +// starting with keywords "public", "static" or "stock" +static __pragma("unread") __pragma("unwritten") Func7(); // error 001: expected token: "-identifier-", but found "__pragma" +public __pragma("unread") __pragma("unwritten") global_var2 = 0; // error 001: expected token: "-identifier-", but found "__pragma" + NakedFunc() { __pragma("naked", "deprecated - use NakedFunc2() instead"); From 1b134d438e2f7892369579dd2696dedd7b521afa Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Sun, 2 May 2021 18:22:25 +0700 Subject: [PATCH 16/27] Only require function definitions to have all specifiers introduced in forward declarations --- source/compiler/sc1.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c index a41e13f9..ca7a7ac7 100644 --- a/source/compiler/sc1.c +++ b/source/compiler/sc1.c @@ -3829,9 +3829,6 @@ static void funcstub(int fnative) sym->usage=(short)(uNATIVE | uRETVALUE | uDEFINE | (sym->usage & uPROTOTYPED)); sym->x.lib=curlibrary; } else { - if (((sym->usage & uDECLPUBLIC)!=0 && !fpublic) || ((sym->usage & uDECLSTATIC)!=0 && !fstatic) - || ((sym->usage & uSTOCK)!=0 && !fstock)) - error(25); /* function heading differs from prototype */ if ((sym->usage & uDEFINE)!=0) { /* if the function has already been defined ("finalized"), we can't accept * any new class specifiers */ @@ -3990,9 +3987,6 @@ static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int fsto sym=fetchfunc(symbolname,tag);/* get a pointer to the function entry */ if (sym==NULL || (sym->usage & uNATIVE)!=0) return TRUE; /* it was recognized as a function declaration, but not as a valid one */ - if (((sym->usage & uDECLPUBLIC)!=0 && !fpublic) || ((sym->usage & uDECLSTATIC)!=0 && !fstatic) - || ((sym->usage & uSTOCK)!=0 && !fstock)) - error(25); /* function heading differs from prototype */ if (fpublic && opertok==0) sym->usage |= (uPUBLIC | uDECLPUBLIC); if (fstatic) { @@ -4045,6 +4039,9 @@ static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int fsto } /* if */ attachstatelist(sym,state_id); /* so it is not a prototype, proceed */ + if (((sym->usage & uDECLPUBLIC)!=0 && !fpublic) || ((sym->usage & uDECLSTATIC)!=0 && !fstatic) + || ((sym->usage & uSTOCK)!=0 && !fstock)) + error(25); /* function heading differs from prototype */ /* if this is a function that is not referred to (this can only be detected * in the second stage), shut code generation off */ if (sc_status==statWRITE && (sym->usage & uREAD)==0 && !fpublic) { From b15eb44c3c187686ccc5a8c0458e34641688fa55 Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Sun, 2 May 2021 20:21:26 +0700 Subject: [PATCH 17/27] Update tests --- source/compiler/tests/class_specifiers_p2.meta | 2 ++ source/compiler/tests/class_specifiers_p2.pwn | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/source/compiler/tests/class_specifiers_p2.meta b/source/compiler/tests/class_specifiers_p2.meta index edafd2b3..3da83d16 100644 --- a/source/compiler/tests/class_specifiers_p2.meta +++ b/source/compiler/tests/class_specifiers_p2.meta @@ -4,5 +4,7 @@ class_specifiers_p2.pwn(4) : error 042: invalid combination of class specifiers class_specifiers_p2.pwn(7) : error 042: invalid combination of class specifiers class_specifiers_p2.pwn(11) : error 010: invalid function or declaration +class_specifiers_p2.pwn(16) : error 025: function heading differs from prototype +class_specifiers_p2.pwn(25) : error 025: function heading differs from prototype """ } diff --git a/source/compiler/tests/class_specifiers_p2.pwn b/source/compiler/tests/class_specifiers_p2.pwn index a85610e4..c458787c 100644 --- a/source/compiler/tests/class_specifiers_p2.pwn +++ b/source/compiler/tests/class_specifiers_p2.pwn @@ -11,4 +11,19 @@ static @var2 = 0; // error 042: invalid combination of class specifiers forward Tag:[2] static stock Func(); // error 010: invalid function or declaration forward static stock Tag:[2] Func2(); // OK +forward Func3(); +public Func3(); // class specifier "public" is introduced; it will be required in the definition +Func3(){} // error 025: function heading differs from prototype + +static Func4(); // class specifier "static" is introduced +forward Func4(); // OK (class specifiers are only mandatory in function definitions, not declarations) +static Func4(){} // OK (class specifier "static" is in place) + +static Func5(){} // Func5() is "finalized"; subsequent forward declarations + // for this function can't introduce any new class specifiers +forward static Func5(); // OK (no new class specifiers) +forward static stock Func5(); // error 025: function heading differs from prototype + +#pragma unused Func4, Func5 + main(){} From cb8ada3dcf8d4d181fff64f77326521bc886045f Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Mon, 3 May 2021 01:16:36 +0700 Subject: [PATCH 18/27] Do not require specifier `stock`, even if it was previously introduced in one of function declarations --- source/compiler/sc1.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c index ca7a7ac7..e1ff6184 100644 --- a/source/compiler/sc1.c +++ b/source/compiler/sc1.c @@ -4039,8 +4039,7 @@ static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int fsto } /* if */ attachstatelist(sym,state_id); /* so it is not a prototype, proceed */ - if (((sym->usage & uDECLPUBLIC)!=0 && !fpublic) || ((sym->usage & uDECLSTATIC)!=0 && !fstatic) - || ((sym->usage & uSTOCK)!=0 && !fstock)) + if (((sym->usage & uDECLPUBLIC)!=0 && !fpublic) || ((sym->usage & uDECLSTATIC)!=0 && !fstatic)) error(25); /* function heading differs from prototype */ /* if this is a function that is not referred to (this can only be detected * in the second stage), shut code generation off */ From 32819a311e2329693ce1a1113cf76b3cd0dd3c93 Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Mon, 3 May 2021 01:24:55 +0700 Subject: [PATCH 19/27] Allow introduction of specifier `stock` in post-definition forward declarations Example: Func(){} forward stock Func(); --- source/compiler/sc1.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c index e1ff6184..7dcc151e 100644 --- a/source/compiler/sc1.c +++ b/source/compiler/sc1.c @@ -3832,8 +3832,7 @@ static void funcstub(int fnative) if ((sym->usage & uDEFINE)!=0) { /* if the function has already been defined ("finalized"), we can't accept * any new class specifiers */ - if ((fpublic && (sym->usage & uDECLPUBLIC)==0) || (fstatic && (sym->usage & uDECLSTATIC)==0) - || (fstock && (sym->usage & uSTOCK)==0)) + if ((fpublic && (sym->usage & uDECLPUBLIC)==0) || (fstatic && (sym->usage & uDECLSTATIC)==0)) error(25); /* function heading differs from prototype */ } else { if (fpublic && opertok==0) @@ -3842,9 +3841,9 @@ static void funcstub(int fnative) sym->usage |= uDECLSTATIC; sym->fnumber=filenum; } /* if */ - if (fstock) - sym->usage|=uSTOCK; } /* if */ + if (fstock) + sym->usage |= uSTOCK; } /* if */ sym->usage|=uFORWARD; check_reparse(sym); From 3f762c78610ac211a294349411e36b7764143b0f Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Mon, 3 May 2021 01:30:31 +0700 Subject: [PATCH 20/27] Update tests --- source/compiler/tests/class_specifiers_p2.pwn | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/source/compiler/tests/class_specifiers_p2.pwn b/source/compiler/tests/class_specifiers_p2.pwn index c458787c..a165fb78 100644 --- a/source/compiler/tests/class_specifiers_p2.pwn +++ b/source/compiler/tests/class_specifiers_p2.pwn @@ -19,11 +19,18 @@ static Func4(); // class specifier "static" is introduced forward Func4(); // OK (class specifiers are only mandatory in function definitions, not declarations) static Func4(){} // OK (class specifier "static" is in place) -static Func5(){} // Func5() is "finalized"; subsequent forward declarations - // for this function can't introduce any new class specifiers -forward static Func5(); // OK (no new class specifiers) +stock Func5(){} // Func5() is "finalized"; subsequent forward declarations + // for this function can't introduce specifiers "static" and "public" +forward stock Func5(); // OK (no new class specifiers) forward static stock Func5(); // error 025: function heading differs from prototype -#pragma unused Func4, Func5 +Func6(){} +forward stock Func6(); // OK (specifier "stock" can be introduced after the definition) + +forward stock Func7(); // specifier "stock" is introduced, but it's not mandatory to use it + // in the function definition +Func7(){} // OK + +#pragma unused Func4, Func5, Func6, Func7 main(){} From e7bd54386320ff5677821885c60585257edcfab3 Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Sat, 15 Jan 2022 23:29:12 +0700 Subject: [PATCH 21/27] Don't accept new class specifiers in "old-style" re-declarations if the function has already been defined --- source/compiler/sc1.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c index 7dcc151e..203a23ad 100644 --- a/source/compiler/sc1.c +++ b/source/compiler/sc1.c @@ -3937,6 +3937,7 @@ static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int fsto cell val,cidx,glbdecl; short filenum; int state_id; + int funcusage; unsigned int bck_attributes; char *bck_deprecate; /* in case the user tries to use __pragma("deprecated") * on a function argument */ @@ -3986,6 +3987,8 @@ static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int fsto sym=fetchfunc(symbolname,tag);/* get a pointer to the function entry */ if (sym==NULL || (sym->usage & uNATIVE)!=0) return TRUE; /* it was recognized as a function declaration, but not as a valid one */ + funcusage=sym->usage; /* before setting flags `uDECLPUBLIC` and `uDECLSTATIC`, + * back up the current usage state, we'll need it later */ if (fpublic && opertok==0) sym->usage |= (uPUBLIC | uDECLPUBLIC); if (fstatic) { @@ -4033,6 +4036,11 @@ static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int fsto error(218); /* old style prototypes used with optional semicolons */ if (state_id!=0) error(231); /* state specification on forward declaration is ignored */ + /* if the function has already been defined ("finalized"), we can't accept + * any new class specifiers */ + if ((sym->usage & uDEFINE)!=0 + && ((fpublic && (funcusage & uDECLPUBLIC)==0) || (fstatic && (funcusage & uDECLSTATIC)==0))) + error(25); /* function heading differs from prototype */ delete_symbols(&loctab,0,TRUE,TRUE); /* prototype is done; forget everything */ return TRUE; } /* if */ From 8c3a09db4cdf667683254be4bfa0690646f32adb Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Sat, 15 Jan 2022 23:31:38 +0700 Subject: [PATCH 22/27] Update tests --- source/compiler/tests/class_specifiers_p2.meta | 1 + source/compiler/tests/class_specifiers_p2.pwn | 1 + 2 files changed, 2 insertions(+) diff --git a/source/compiler/tests/class_specifiers_p2.meta b/source/compiler/tests/class_specifiers_p2.meta index 3da83d16..8b21cac2 100644 --- a/source/compiler/tests/class_specifiers_p2.meta +++ b/source/compiler/tests/class_specifiers_p2.meta @@ -6,5 +6,6 @@ class_specifiers_p2.pwn(7) : error 042: invalid combination of class specifiers class_specifiers_p2.pwn(11) : error 010: invalid function or declaration class_specifiers_p2.pwn(16) : error 025: function heading differs from prototype class_specifiers_p2.pwn(25) : error 025: function heading differs from prototype +class_specifiers_p2.pwn(26) : error 025: function heading differs from prototype """ } diff --git a/source/compiler/tests/class_specifiers_p2.pwn b/source/compiler/tests/class_specifiers_p2.pwn index a165fb78..a2c4dba3 100644 --- a/source/compiler/tests/class_specifiers_p2.pwn +++ b/source/compiler/tests/class_specifiers_p2.pwn @@ -23,6 +23,7 @@ stock Func5(){} // Func5() is "finalized"; subsequent forward declarations // for this function can't introduce specifiers "static" and "public" forward stock Func5(); // OK (no new class specifiers) forward static stock Func5(); // error 025: function heading differs from prototype +static stock Func5(); // error 025: function heading differs from prototype Func6(){} forward stock Func6(); // OK (specifier "stock" can be introduced after the definition) From e649db0bb21a1cd730a928ae12259ec3fb01a06f Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Sat, 19 Mar 2022 22:05:20 +0700 Subject: [PATCH 23/27] Allow function class specifiers to be "additive", so they aren't required in following function re-declarations or definition --- source/compiler/sc1.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c index 203a23ad..0daa6f6a 100644 --- a/source/compiler/sc1.c +++ b/source/compiler/sc1.c @@ -4046,8 +4046,6 @@ static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int fsto } /* if */ attachstatelist(sym,state_id); /* so it is not a prototype, proceed */ - if (((sym->usage & uDECLPUBLIC)!=0 && !fpublic) || ((sym->usage & uDECLSTATIC)!=0 && !fstatic)) - error(25); /* function heading differs from prototype */ /* if this is a function that is not referred to (this can only be detected * in the second stage), shut code generation off */ if (sc_status==statWRITE && (sym->usage & uREAD)==0 && !fpublic) { From 6d10a24e10c06dd5bfa93c3d386d6c4d17f3376d Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Sat, 19 Mar 2022 22:35:07 +0700 Subject: [PATCH 24/27] Clarify that specifier `stock` can be accepted even after the function is already defined --- source/compiler/sc1.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c index 0daa6f6a..942a4f20 100644 --- a/source/compiler/sc1.c +++ b/source/compiler/sc1.c @@ -4037,7 +4037,8 @@ static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int fsto if (state_id!=0) error(231); /* state specification on forward declaration is ignored */ /* if the function has already been defined ("finalized"), we can't accept - * any new class specifiers */ + * any new class specifiers (except for 'stock', as it doesn't affect code + * generation) */ if ((sym->usage & uDEFINE)!=0 && ((fpublic && (funcusage & uDECLPUBLIC)==0) || (fstatic && (funcusage & uDECLSTATIC)==0))) error(25); /* function heading differs from prototype */ From daad976e1f3baa816a704542ecc4002a5b479ba0 Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Sat, 19 Mar 2022 22:51:23 +0700 Subject: [PATCH 25/27] Detect invalid combinations of "static" and "public" when each specifier is introduced in its own function declaration or definition, e.g.: ``` forward static Func(); forward public Func(); ``` --- source/compiler/sc1.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c index 942a4f20..3783403d 100644 --- a/source/compiler/sc1.c +++ b/source/compiler/sc1.c @@ -3833,8 +3833,10 @@ static void funcstub(int fnative) /* if the function has already been defined ("finalized"), we can't accept * any new class specifiers */ if ((fpublic && (sym->usage & uDECLPUBLIC)==0) || (fstatic && (sym->usage & uDECLSTATIC)==0)) - error(25); /* function heading differs from prototype */ + error(25); /* function heading differs from prototype */ } else { + if ((fpublic && (sym->usage & uDECLSTATIC)!=0) || (fstatic && (sym->usage & uDECLPUBLIC)!=0)) + error(42); /* invalid combination of class specifiers */ if (fpublic && opertok==0) sym->usage |= (uPUBLIC | uDECLPUBLIC); if (fstatic) { @@ -3989,6 +3991,8 @@ static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int fsto return TRUE; /* it was recognized as a function declaration, but not as a valid one */ funcusage=sym->usage; /* before setting flags `uDECLPUBLIC` and `uDECLSTATIC`, * back up the current usage state, we'll need it later */ + if ((fpublic && (sym->usage & uDECLSTATIC)!=0) || (fstatic && (sym->usage & uDECLPUBLIC)!=0)) + error(42); /* invalid combination of class specifiers */ if (fpublic && opertok==0) sym->usage |= (uPUBLIC | uDECLPUBLIC); if (fstatic) { From af265da94ac90b5be5f4e726808f19d293a8ac22 Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Sat, 19 Mar 2022 23:18:15 +0700 Subject: [PATCH 26/27] Don't repeat error 042 twice when a function is defined as `static` and its name starts with `@` --- source/compiler/sc1.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/compiler/sc1.c b/source/compiler/sc1.c index 3783403d..165baa0e 100644 --- a/source/compiler/sc1.c +++ b/source/compiler/sc1.c @@ -3991,7 +3991,8 @@ static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int fsto return TRUE; /* it was recognized as a function declaration, but not as a valid one */ funcusage=sym->usage; /* before setting flags `uDECLPUBLIC` and `uDECLSTATIC`, * back up the current usage state, we'll need it later */ - if ((fpublic && (sym->usage & uDECLSTATIC)!=0) || (fstatic && (sym->usage & uDECLPUBLIC)!=0)) + if (symbolname[0]!=PUBLIC_CHAR && ((fpublic && (sym->usage & uDECLSTATIC)!=0) + || (fstatic && (sym->usage & uDECLPUBLIC)!=0))) error(42); /* invalid combination of class specifiers */ if (fpublic && opertok==0) sym->usage |= (uPUBLIC | uDECLPUBLIC); From 8a9d5cc1ee713c0e63fd2085788317045b6fca8c Mon Sep 17 00:00:00 2001 From: Stanislav Gromov Date: Sat, 19 Mar 2022 23:43:27 +0700 Subject: [PATCH 27/27] Update tests --- .../compiler/tests/class_specifiers_p2.meta | 7 ++-- source/compiler/tests/class_specifiers_p2.pwn | 40 +++++++++++-------- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/source/compiler/tests/class_specifiers_p2.meta b/source/compiler/tests/class_specifiers_p2.meta index 8b21cac2..90fc4036 100644 --- a/source/compiler/tests/class_specifiers_p2.meta +++ b/source/compiler/tests/class_specifiers_p2.meta @@ -4,8 +4,9 @@ class_specifiers_p2.pwn(4) : error 042: invalid combination of class specifiers class_specifiers_p2.pwn(7) : error 042: invalid combination of class specifiers class_specifiers_p2.pwn(11) : error 010: invalid function or declaration -class_specifiers_p2.pwn(16) : error 025: function heading differs from prototype -class_specifiers_p2.pwn(25) : error 025: function heading differs from prototype -class_specifiers_p2.pwn(26) : error 025: function heading differs from prototype +class_specifiers_p2.pwn(29) : error 042: invalid combination of class specifiers +class_specifiers_p2.pwn(30) : error 042: invalid combination of class specifiers +class_specifiers_p2.pwn(35) : error 025: function heading differs from prototype +class_specifiers_p2.pwn(36) : error 025: function heading differs from prototype """ } diff --git a/source/compiler/tests/class_specifiers_p2.pwn b/source/compiler/tests/class_specifiers_p2.pwn index a2c4dba3..d929846b 100644 --- a/source/compiler/tests/class_specifiers_p2.pwn +++ b/source/compiler/tests/class_specifiers_p2.pwn @@ -12,26 +12,34 @@ forward Tag:[2] static stock Func(); // error 010: invalid function or declarat forward static stock Tag:[2] Func2(); // OK forward Func3(); -public Func3(); // class specifier "public" is introduced; it will be required in the definition -Func3(){} // error 025: function heading differs from prototype +stock Func3(); // class specifier "stock" is added +Func3(){} // OK (the function is implicitly defined as "stock") -static Func4(); // class specifier "static" is introduced -forward Func4(); // OK (class specifiers are only mandatory in function definitions, not declarations) -static Func4(){} // OK (class specifier "static" is in place) +forward Func4(); +public Func4(); // class specifier "public" is added +Func4(){} // OK (the function is implicitly defined as "public") -stock Func5(){} // Func5() is "finalized"; subsequent forward declarations - // for this function can't introduce specifiers "static" and "public" -forward stock Func5(); // OK (no new class specifiers) -forward static stock Func5(); // error 025: function heading differs from prototype -static stock Func5(); // error 025: function heading differs from prototype +forward Func5(); +static Func5(); // class specifier "static" is added +Func5(){} // OK (the function is implicitly defined as "static") + +// Func6() is declared as "static", so any subsequent re-declarations +// of this function with specifier "public" should be treated as errors +forward static Func6(); +forward public Func6(); // error 025: function heading differs from prototype +public Func6(){} // error 025: function heading differs from prototype -Func6(){} -forward stock Func6(); // OK (specifier "stock" can be introduced after the definition) +stock Func7(){} // Func4() is "finalized"; subsequent forward declarations + // for this function can't introduce specifiers "static" and "public" +forward stock Func7(); // OK (no new class specifiers) +forward static stock Func7(); // error 025: function heading differs from prototype +static stock Func7(); // error 025: function heading differs from prototype -forward stock Func7(); // specifier "stock" is introduced, but it's not mandatory to use it - // in the function definition -Func7(){} // OK +// Func8() is "finalized", but specifier "stock" can be added even after the definition, +// as it doesn't affect code generation (it only disables warning 203 for the function) +Func8(){} +forward stock Func8(); // OK -#pragma unused Func4, Func5, Func6, Func7 +#pragma unused Func3, Func5, Func7, Func8 main(){}