Skip to content

Commit 6781afe

Browse files
committed
Merge remote-tracking branch 'remotes/Daniel-Cortez/loop-diagnostics' into dev
2 parents 001f483 + 64b2cda commit 6781afe

File tree

8 files changed

+367
-25
lines changed

8 files changed

+367
-25
lines changed

source/compiler/sc.h

+22-8
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,16 @@ typedef struct s_symbol {
229229
#define uRETNONE 0x010
230230
/* uASSIGNED indicates that a value assigned to the variable is not used yet */
231231
#define uASSIGNED 0x080
232+
/* uLOOPVAR is set when a variable is read inside of a loop condition. This is
233+
* used to detect situations when a variable is used in a loop condition, but
234+
* not modified inside of a loop body. */
235+
#define uLOOPVAR 0x1000
236+
/* uNOLOOPVAR is set when a variable is
237+
* * modified inside of a loop condition before being read, or
238+
* * used in an enclosing loop and should be excluded from checks in an inner loop,
239+
* so the compiler would know it shouldn't set the uLOOPVAR flag when the variable
240+
* is read inside a loop condition */
241+
#define uNOLOOPVAR 0x2000
232242

233243
#define flagDEPRECATED 0x01 /* symbol is deprecated (avoid use) */
234244
#define flagNAKED 0x10 /* function is naked */
@@ -300,15 +310,17 @@ typedef struct s_valuepair {
300310
long second;
301311
} valuepair;
302312

303-
/* struct "assigninfo" is used to synchronize the status of assignments that
304-
* were made in multiple "if" and "switch" branches, so the compiler could
305-
* detect unused assignments in all of those branches, not only the last one */
313+
/* struct "symstate" is used to:
314+
* * synchronize the status of assignments between all "if" branches or "switch"
315+
* cases, so the compiler could detect unused assignments in all of those
316+
* branches/cases, not only in the last one;
317+
* * back up the "uNOLOOPVAR" flag when scanning for variables that were used
318+
* in a loop exit condition, but weren't modified inside the loop body */
306319
typedef struct s_assigninfo {
307-
int unused; /* true if the variable has an unused value assigned to it
308-
* in one of the branches" */
309320
int lnumber; /* line number of the first unused assignment made in one of
310321
* the branches (used for error messages) */
311-
} assigninfo;
322+
short usage; /* usage flags to memoize (currently only uASSIGNED) */
323+
} symstate;
312324

313325
/* macros for code generation */
314326
#define opcodes(n) ((n)*sizeof(cell)) /* opcode size */
@@ -733,8 +745,8 @@ SC_FUNC int refer_symbol(symbol *entry,symbol *bywhom);
733745
SC_FUNC void markusage(symbol *sym,int usage);
734746
SC_FUNC void markinitialized(symbol *sym,int assignment);
735747
SC_FUNC void clearassignments(int fromlevel);
736-
SC_FUNC void memoizeassignments(int fromlevel,assigninfo **assignments);
737-
SC_FUNC void restoreassignments(int fromlevel,assigninfo *assignments);
748+
SC_FUNC void memoizeassignments(int fromlevel,symstate **assignments);
749+
SC_FUNC void restoreassignments(int fromlevel,symstate *assignments);
738750
SC_FUNC void rename_symbol(symbol *sym,const char *newname);
739751
SC_FUNC symbol *findglb(const char *name,int filter);
740752
SC_FUNC symbol *findloc(const char *name);
@@ -1009,6 +1021,8 @@ SC_VDECL int pc_retheap; /* heap space (in bytes) to be manually freed when
10091021
SC_VDECL int pc_nestlevel; /* number of active (open) compound statements */
10101022
SC_VDECL unsigned int pc_attributes;/* currently set attribute flags (for the "__pragma" operator) */
10111023
SC_VDECL int pc_ispackedstr; /* true if the last tokenized string is packed */
1024+
SC_VDECL int pc_loopcond; /* equals to 'tFOR', 'tWHILE' or 'tDO' if the current expression is a loop condition, zero otherwise */
1025+
SC_VDECL int pc_numloopvars; /* number of variables used inside a loop condition */
10121026

10131027
SC_VDECL char *sc_tokens[];
10141028

source/compiler/sc1.c

+141-5
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ static void make_report(symbol *root,FILE *log,char *sourcefile);
116116
static void reduce_referrers(symbol *root);
117117
static long max_stacksize(symbol *root,int *recursion);
118118
static int testsymbols(symbol *root,int level,int testlabs,int testconst);
119+
static void scanloopvariables(symstate **loopvars,int dowhile);
120+
static void testloopvariables(symstate *loopvars,int dowhile,int line);
119121
static void destructsymbols(symbol *root,int level);
120122
static constvalue *find_constval_byval(constvalue_root *table,cell val);
121123
static symbol *fetchlab(char *name);
@@ -919,6 +921,7 @@ static void resetglobals(void)
919921
pc_naked=FALSE;
920922
pc_retexpr=FALSE;
921923
pc_attributes=0;
924+
pc_loopcond=0;
922925
emit_flags=0;
923926
emit_stgbuf_idx=-1;
924927
}
@@ -5173,6 +5176,112 @@ static int testsymbols(symbol *root,int level,int testlabs,int testconst)
51735176
return entry;
51745177
}
51755178

5179+
static void scanloopvariables(symstate **loopvars,int dowhile)
5180+
{
5181+
symbol *start,*sym;
5182+
int num;
5183+
5184+
/* error messages are only printed on the "writing" pass,
5185+
* so if we are not writing yet, then we have a quick exit */
5186+
if (sc_status!=statWRITE)
5187+
return;
5188+
5189+
/* if there's no enclosing loop (only one active loop entry, which is the
5190+
* current loop), and the current loop is not 'do-while', then we don't need
5191+
* to memoize usage flags for local variables, so we have an early exit */
5192+
if (wqptr-wqSIZE==wq && !dowhile)
5193+
return;
5194+
5195+
/* skip labels */
5196+
start=&loctab;
5197+
while ((start=start->next)!=NULL && start->ident==iLABEL)
5198+
/* nothing */;
5199+
/* if there are no other local symbols, we have an early exit */
5200+
if (start==NULL)
5201+
return;
5202+
5203+
/* count the number of local symbols */
5204+
for (num=0,sym=start; sym!=NULL; num++,sym=sym->next)
5205+
/* nothing */;
5206+
5207+
assert(*loopvars==NULL);
5208+
assert(num!=0);
5209+
*loopvars=(symstate *)calloc((size_t)num,sizeof(symstate));
5210+
if (*loopvars==NULL)
5211+
error(103); /* insufficient memory */
5212+
5213+
for (num=0,sym=start; sym!=NULL; num++,sym=sym->next) {
5214+
/* If the variable already has the uLOOPVAR flag set (from being used
5215+
* in an enclosing loop), we have to set the uNOLOOPVAR to exclude it
5216+
* from checks in the current loop, ... */
5217+
if ((sym->ident==iVARIABLE || sym->ident==iREFERENCE)
5218+
&& (dowhile || (sym->usage & uLOOPVAR)!=0)) {
5219+
/* ... but it might be already set from an enclosing loop as well, so we
5220+
* have to temporarily store it in "loopvars[num]" first. Also, if this is
5221+
* a 'do-while' loop, we need to memoize and unset the 'uWRITTEN' flag, so
5222+
* later when analyzing the loop condition (which comes after the loop
5223+
* body) we'll be able to determine if the variable was modified inside
5224+
* the loop body by checking if the 'uWRITTEN' flag is set. */
5225+
(*loopvars)[num].usage |= (sym->usage & (uNOLOOPVAR | uWRITTEN));
5226+
sym->usage &= ~uWRITTEN;
5227+
if (wqptr-wqSIZE!=wq)
5228+
sym->usage |= uNOLOOPVAR;
5229+
} /* if */
5230+
} /* if */
5231+
}
5232+
5233+
static void testloopvariables(symstate *loopvars,int dowhile,int line)
5234+
{
5235+
symbol *start,*sym;
5236+
int num,warnnum=0;
5237+
5238+
/* the error messages are only printed on the "writing" pass,
5239+
* so if we are not writing yet, then we have a quick exit */
5240+
if (sc_status!=statWRITE)
5241+
return;
5242+
5243+
/* skip labels */
5244+
start=&loctab;
5245+
while ((start=start->next)!=NULL && start->ident==iLABEL)
5246+
/* nothing */;
5247+
5248+
/* decrement pc_numloopvars by 1 for each variable that wasn't modified
5249+
* inside the loop body; if pc_numloopvars gets zeroed after this, it would
5250+
* mean none of the variables used inside the loop condition were modified */
5251+
if (pc_numloopvars!=0) {
5252+
warnnum=(pc_numloopvars==1) ? 250 : 251;
5253+
for (sym=start; sym!=NULL; sym=sym->next)
5254+
if ((sym->ident==iVARIABLE || sym->ident==iREFERENCE)
5255+
&& (sym->usage & (uLOOPVAR | uNOLOOPVAR))==uLOOPVAR
5256+
&& (!dowhile || (sym->usage & uWRITTEN)==0))
5257+
pc_numloopvars--;
5258+
if (pc_numloopvars==0 && warnnum==251) {
5259+
errorset(sSETPOS,line);
5260+
error(251); /* none of the variables used in loop condition are modified in loop body */
5261+
errorset(sSETPOS,-1);
5262+
} /* if */
5263+
} /* if */
5264+
5265+
for (num=0,sym=start; sym!=NULL; num++,sym=sym->next) {
5266+
if (sym->ident==iVARIABLE || sym->ident==iREFERENCE) {
5267+
if ((sym->usage & (uLOOPVAR | uNOLOOPVAR))==uLOOPVAR) {
5268+
sym->usage &= ~uLOOPVAR;
5269+
/* warn only if none of the variables used inside the loop condition
5270+
* were modified inside the loop body */
5271+
if (pc_numloopvars==0 && warnnum==250) {
5272+
errorset(sSETPOS,line);
5273+
error(250,sym->name); /* variable used in loop condition not modified in loop body */
5274+
errorset(sSETPOS,-1);
5275+
} /* if */
5276+
} /* if */
5277+
sym->usage &= ~uNOLOOPVAR;
5278+
if (loopvars!=NULL)
5279+
sym->usage |= loopvars[num].usage;
5280+
} /* if */
5281+
} /* for */
5282+
free(loopvars);
5283+
}
5284+
51765285
static cell calc_array_datasize(symbol *sym, cell *offset)
51775286
{
51785287
cell length;
@@ -5883,7 +5992,7 @@ static int doif(void)
58835992
int ifindent;
58845993
int lastst_true;
58855994
int returnst=tIF;
5886-
assigninfo *assignments=NULL;
5995+
symstate *assignments=NULL;
58875996

58885997
lastst=0; /* reset the last statement */
58895998
ifindent=stmtindent; /* save the indent of the "if" instruction */
@@ -5924,28 +6033,37 @@ static int doif(void)
59246033
static int dowhile(void)
59256034
{
59266035
int wq[wqSIZE]; /* allocate local queue */
5927-
int save_endlessloop,retcode;
6036+
int save_endlessloop,save_numloopvars,retcode;
6037+
int loopline=fline;
6038+
symstate *loopvars=NULL;
59286039

59296040
save_endlessloop=endlessloop;
6041+
save_numloopvars=pc_numloopvars;
6042+
pc_numloopvars=0;
59306043
addwhile(wq); /* add entry to queue for "break" */
59316044
setlabel(wq[wqLOOP]); /* loop label */
59326045
/* The debugger uses the "break" opcode to be able to "break" out of
59336046
* a loop. To make sure that each loop has a break opcode, even for the
59346047
* tiniest loop, set it below the top of the loop
59356048
*/
59366049
setline(TRUE);
6050+
scanloopvariables(&loopvars,FALSE);
59376051
pc_nestlevel++; /* temporarily increase the "compound statement" nesting level,
59386052
* so any assignments made inside the loop control expression
59396053
* could be cleaned up later */
6054+
pc_loopcond=tWHILE;
59406055
endlessloop=test(wq[wqEXIT],TEST_DO,FALSE);/* branch to wq[wqEXIT] if false */
6056+
pc_loopcond=0;
59416057
pc_nestlevel--;
59426058
statement(NULL,FALSE); /* if so, do a statement */
59436059
clearassignments(pc_nestlevel+1);
6060+
testloopvariables(loopvars,FALSE,loopline);
59446061
jumplabel(wq[wqLOOP]); /* and loop to "while" start */
59456062
setlabel(wq[wqEXIT]); /* exit label */
59466063
delwhile(); /* delete queue entry */
59476064

59486065
retcode=endlessloop ? tENDLESS : tWHILE;
6066+
pc_numloopvars=save_numloopvars;
59496067
endlessloop=save_endlessloop;
59506068
return retcode;
59516069
}
@@ -5957,28 +6075,37 @@ static int dowhile(void)
59576075
static int dodo(void)
59586076
{
59596077
int wq[wqSIZE],top;
5960-
int save_endlessloop,retcode;
6078+
int save_endlessloop,save_numloopvars,retcode;
6079+
int loopline=fline;
6080+
symstate *loopvars=NULL;
59616081

59626082
save_endlessloop=endlessloop;
6083+
save_numloopvars=pc_numloopvars;
6084+
pc_numloopvars=0;
59636085
addwhile(wq); /* see "dowhile" for more info */
59646086
top=getlabel(); /* make a label first */
59656087
setlabel(top); /* loop label */
6088+
scanloopvariables(&loopvars,TRUE);
59666089
statement(NULL,FALSE);
59676090
needtoken(tWHILE);
59686091
setlabel(wq[wqLOOP]); /* "continue" always jumps to WQLOOP. */
59696092
setline(TRUE);
59706093
pc_nestlevel++; /* temporarily increase the "compound statement" nesting level,
59716094
* so any assignments made inside the loop control expression
59726095
* could be cleaned up later */
6096+
pc_loopcond=tDO;
59736097
endlessloop=test(wq[wqEXIT],TEST_OPT,FALSE);
6098+
pc_loopcond=0;
59746099
pc_nestlevel--;
59756100
clearassignments(pc_nestlevel+1);
6101+
testloopvariables(loopvars,TRUE,loopline);
59766102
jumplabel(top);
59776103
setlabel(wq[wqEXIT]);
59786104
delwhile();
59796105
needtoken(tTERM);
59806106

59816107
retcode=endlessloop ? tENDLESS : tDO;
6108+
pc_numloopvars=save_numloopvars;
59826109
endlessloop=save_endlessloop;
59836110
return retcode;
59846111
}
@@ -5987,13 +6114,17 @@ static int dofor(void)
59876114
{
59886115
int wq[wqSIZE],skiplab;
59896116
cell save_decl;
5990-
int save_nestlevel,save_endlessloop;
6117+
int save_nestlevel,save_endlessloop,save_numloopvars;
59916118
int index,endtok;
59926119
int *ptr;
6120+
int loopline=fline;
6121+
symstate *loopvars=NULL;
59936122

59946123
save_decl=declared;
59956124
save_nestlevel=pc_nestlevel;
59966125
save_endlessloop=endlessloop;
6126+
save_numloopvars=pc_numloopvars;
6127+
pc_numloopvars=0;
59976128

59986129
addwhile(wq);
59996130
skiplab=getlabel();
@@ -6026,6 +6157,7 @@ static int dofor(void)
60266157
jumplabel(skiplab); /* skip expression 3 1st time */
60276158
setlabel(wq[wqLOOP]); /* "continue" goes to this label: expr3 */
60286159
setline(TRUE);
6160+
scanloopvariables(&loopvars,FALSE);
60296161
/* Expressions 2 and 3 are reversed in the generated code: expression 3
60306162
* precedes expression 2. When parsing, the code is buffered and marks for
60316163
* the start of each expression are insterted in the buffer.
@@ -6040,7 +6172,9 @@ static int dofor(void)
60406172
if (matchtoken(';')) {
60416173
endlessloop=1;
60426174
} else {
6175+
pc_loopcond=tFOR;
60436176
endlessloop=test(wq[wqEXIT],TEST_PLAIN,FALSE);/* expression 2 (jump to wq[wqEXIT] if false) */
6177+
pc_loopcond=0;
60446178
needtoken(';');
60456179
} /* if */
60466180
stgmark((char)(sEXPRSTART+1)); /* mark start of 3th expression in stage */
@@ -6053,6 +6187,7 @@ static int dofor(void)
60536187
stgset(FALSE); /* stop staging */
60546188
statement(NULL,FALSE);
60556189
clearassignments(save_nestlevel+1);
6190+
testloopvariables(loopvars,FALSE,loopline);
60566191
jumplabel(wq[wqLOOP]);
60576192
setlabel(wq[wqEXIT]);
60586193
delwhile();
@@ -6071,6 +6206,7 @@ static int dofor(void)
60716206
pc_nestlevel=save_nestlevel; /* reset 'compound statement' nesting level */
60726207

60736208
index=endlessloop ? tENDLESS : tFOR;
6209+
pc_numloopvars=save_numloopvars;
60746210
endlessloop=save_endlessloop;
60756211
return index;
60766212
}
@@ -6103,7 +6239,7 @@ static int doswitch(void)
61036239
constvalue_root caselist = { NULL, NULL}; /* case list starts empty */
61046240
constvalue *cse,*csp,*newval;
61056241
char labelname[sNAMEMAX+1];
6106-
assigninfo *assignments=NULL;
6242+
symstate *assignments=NULL;
61076243

61086244
endtok= matchtoken('(') ? ')' : tDO;
61096245
ident=doexpr(TRUE,FALSE,FALSE,FALSE,&swtag,NULL,TRUE,NULL); /* evaluate switch expression */

0 commit comments

Comments
 (0)