1616module module_analyser;
1717
1818import ast local;
19+ import ctv_analyser;
1920import label_vector local;
2021import scope;
2122
22- fn bool hasReturn(const Stmt* s) {
23+ type Flow enum u8 {
24+ Next,
25+ Return,
26+ Break,
27+ Continue,
28+ Goto,
29+ NoReturn,
30+ Error,
31+ }
2332
24- switch (s.getKind()) {
25- case Return:
26- return true;
27- case If:
28- const IfStmt* i = (IfStmt*)s;
29- if (!hasReturn(i.getThen())) return false;
30- const Stmt* e = i.getElse();
31- if (e && hasReturn(e)) return true;
32- break;
33- case While:
34- // TODO only allow while(true/1), no breaks and only return
35- break;
36- case For:
37- // TODO
38- break;
39- case Switch:
40- // TODO
41- break;
42- case Label:
43- // TODO
44- const LabelStmt* ls = (LabelStmt*)s;
45- const Stmt* lss = ls.getStmt();
46- if (!lss) return false;
47- return hasReturn(lss);
48- case Compound:
49- const CompoundStmt* cs = (CompoundStmt*)s;
50- const Stmt* last = cs.getLastStmt();
51- if (!last) return false;
52- return hasReturn(last);
53- default:
54- break;
55- }
33+ type FlowBits u32;
34+
35+ const FlowBits FlowNext = 1 << Flow.Next;
36+ const FlowBits FlowReturn = 1 << Flow.Return;
37+ const FlowBits FlowBreak = 1 << Flow.Break;
38+ const FlowBits FlowContinue = 1 << Flow.Continue;
39+ const FlowBits FlowGoto = 1 << Flow.Goto;
40+ const FlowBits FlowNoReturn = 1 << Flow.NoReturn;
41+ const FlowBits FlowError = 1 << Flow.Error;
5642
57- return false;
43+ fn Expr* getCondExpr(const Stmt* cond) {
44+ if (cond) {
45+ if (cond.isExpr()) return cast<Expr*>(cond);
46+ if (cond.isDecl()) return cast<DeclStmt*>(cond).getDecl(0).getInit();
47+ }
48+ return nil;
5849}
5950
60- fn void Analyser.analyseStmt(Analyser* ma, Stmt* s, bool checkEffect) {
61- if (ma.scope.isUnreachable() && s.getKind() != StmtKind.Label) {
62- ma.warn(s.getLoc(), "unreachable code");
51+ fn FlowBits Analyser.analyseStmt(Analyser* ma, Stmt* s, bool checkEffect) {
52+ FlowBits flow = 0;
53+ if (ma.scope.isUnreachable()) {
54+ if (s.getKind() != StmtKind.Label && !ma.warnings.no_unreachable_code) {
55+ ma.warn(s.getLoc(), "unreachable code");
56+ }
57+ ma.scope.setReachable(); // prevent multiple warnings
6358 }
6459 switch (s.getKind()) {
6560 case Return:
6661 ma.analyseReturnStmt(s);
67- ma.scope.setUnreachable(); // Note: can have label after that is reachable!
68- break ;
62+ ma.scope.setUnreachable();
63+ return FlowReturn ;
6964 case Expr:
7065 // TODO need a different set, since this one uses the Stack of Globals
7166 ma.analyseExpr((Expr**)&s, false, 0);
7267 Expr* e = (Expr*)s;
7368 if (checkEffect && !e.hasEffect()) ma.errorRange(e.getLoc(), e.getRange(), "expression without effect");
74- break;
69+ return FlowNext; // TODO: check for call to noreturn function
7570 case If:
76- ma.analyseIfStmt(s);
71+ flow = ma.analyseIfStmt(s);
7772 break;
7873 case While:
79- ma.analyseWhileStmt(s);
74+ flow = ma.analyseWhileStmt(s);
8075 break;
8176 case For:
82- ma.analyseForStmt(s);
77+ flow = ma.analyseForStmt(s);
8378 break;
8479 case Switch:
85- ma.analyseSwitchStmt(s);
80+ flow = ma.analyseSwitchStmt(s);
8681 break;
8782 case Break:
8883 ma.analyseBreakStmt(s);
8984 ma.scope.setUnreachable();
90- break ;
85+ return FlowBreak ;
9186 case Continue:
9287 ma.analyseContinueStmt(s);
9388 ma.scope.setUnreachable();
94- break ;
89+ return FlowContinue ;
9590 case Fallthrough:
9691 ma.analyseFallthroughStmt(s);
97- break ;
92+ return FlowNext ;
9893 case Label:
99- ma.scope.setReachable();
100- ma.analyseLabelStmt(s);
94+ flow = ma.analyseLabelStmt(s);
10195 break;
10296 case Goto:
10397 ma.analyseGotoStmt(s);
10498 ma.scope.setUnreachable();
105- break ;
99+ return FlowGoto ;
106100 case Compound:
107101 ma.scope.enter(scope.Decl);
108- ma.analyseCompoundStmt((CompoundStmt*)s);
102+ flow = ma.analyseCompoundStmt((CompoundStmt*)s);
109103 ma.scope.exit(ma.has_error);
110104 break;
111105 case Decl:
112106 ma.analyseDeclStmt(s);
113- break ;
107+ return FlowNext ;
114108 case Asm:
115109 ma.analyseAsmStmt(s);
116- break ;
110+ return FlowNext ;
117111 case Assert:
118112 ma.analyseAssertStmt(s);
119- break ;
113+ return FlowNext ;
120114 }
115+ if (!(flow & FlowNext)) ma.scope.setUnreachable();
116+ return flow;
121117}
122118
123119fn void Analyser.analyseBreakStmt(Analyser* ma, Stmt* s) {
@@ -137,7 +133,7 @@ fn void Analyser.analyseFallthroughStmt(Analyser* ma, Stmt* s) {
137133 ma.error(s.getLoc(), "'fallthrough' statement cannot be used here");
138134}
139135
140- fn void Analyser.analyseLabelStmt(Analyser* ma, Stmt* s) {
136+ fn FlowBits Analyser.analyseLabelStmt(Analyser* ma, Stmt* s) {
141137 LabelStmt* ls = (LabelStmt*)s;
142138 u32 name = ls.getNameIdx();
143139
@@ -157,7 +153,8 @@ fn void Analyser.analyseLabelStmt(Analyser* ma, Stmt* s) {
157153 }
158154
159155 Stmt* lss = ls.getStmt();
160- if (lss) ma.analyseStmt(lss, true);
156+ if (!lss) return FlowNext;
157+ return ma.analyseStmt(lss, true);
161158}
162159
163160fn void Analyser.analyseGotoStmt(Analyser* ma, Stmt* s) {
@@ -174,14 +171,18 @@ fn void Analyser.analyseGotoStmt(Analyser* ma, Stmt* s) {
174171 }
175172}
176173
177- fn void Analyser.analyseCompoundStmt(Analyser* ma, CompoundStmt* c) {
174+ fn FlowBits Analyser.analyseCompoundStmt(Analyser* ma, CompoundStmt* c) {
175+ FlowBits flow = 0;
176+ FlowBits flow2 = FlowNext;
178177 u32 count = c.getCount();
179178 Stmt** stmts = c.getStmts();
180179 for (u32 i=0; i<count; i++) {
181180 Stmt* s = stmts[i];
182- ma.analyseStmt(s, true);
183- if (ma.has_error) break;
181+ flow2 = ma.analyseStmt(s, true);
182+ flow |= flow2 & (FlowReturn | FlowBreak | FlowContinue | FlowGoto | FlowNoReturn | FlowError);
183+ if (ma.has_error) return flow;
184184 }
185+ return flow | flow2; // return FlowNext if last statement has FlowNext or empty block
185186}
186187
187188fn QualType Analyser.analyseCondition(Analyser* ma, Stmt** s_ptr, bool check_assign) {
@@ -212,28 +213,34 @@ fn QualType Analyser.analyseCondition(Analyser* ma, Stmt** s_ptr, bool check_ass
212213 return qt;
213214}
214215
215- fn void Analyser.analyseIfStmt(Analyser* ma, Stmt* s) {
216+ fn FlowBits Analyser.analyseIfStmt(Analyser* ma, Stmt* s) {
217+ FlowBits flow = FlowNext;
216218 IfStmt* i = (IfStmt*)s;
217219 ma.scope.enter(scope.Decl);
218220
219221 ma.analyseCondition(i.getCond2(), true);
220222 if (ma.has_error) goto done;
223+ // TODO: handle constant conditions
221224
222225 ma.scope.enter(scope.Decl);
223- ma.analyseStmt(i.getThen(), true);
226+ flow = ma.analyseStmt(i.getThen(), true);
224227 ma.scope.exit(ma.has_error);
225228
229+ FlowBits flow2 = FlowNext;
226230 Stmt* else_ = i.getElse();
227231 if (else_) {
228232 ma.scope.enter(scope.Decl);
229- ma.analyseStmt(else_, true);
233+ flow2 = ma.analyseStmt(else_, true);
230234 ma.scope.exit(ma.has_error);
231235 }
236+ flow |= flow2;
232237done:
233238 ma.scope.exit(ma.has_error);
239+ return flow;
234240}
235241
236- fn void Analyser.analyseForStmt(Analyser* ma, Stmt* s) {
242+ fn FlowBits Analyser.analyseForStmt(Analyser* ma, Stmt* s) {
243+ FlowBits flow = FlowNext;
237244 ForStmt* f = (ForStmt*)s;
238245
239246 ma.scope.enter(scope.Break | scope.Continue | scope.Decl | scope.Control);
@@ -248,6 +255,16 @@ fn void Analyser.analyseForStmt(Analyser* ma, Stmt* s) {
248255 QualType qt = ma.analyseExpr(cond, true, RHS);
249256 if (qt.isInvalid()) goto done;
250257 ma.checker.check(builtins[BuiltinKind.Bool], qt, cond, (*cond).getLoc());
258+ if ((*cond).isCtv()) {
259+ Value v = ctv_analyser.get_value((*cond));
260+ if (v.isZero()) {
261+ // TODO: body is never reached
262+ } else {
263+ flow = 0;
264+ }
265+ }
266+ } else {
267+ flow = 0;
251268 }
252269
253270 Expr** cont = f.getCont2();
@@ -256,22 +273,40 @@ fn void Analyser.analyseForStmt(Analyser* ma, Stmt* s) {
256273 if (qt.isInvalid()) goto done;
257274 }
258275
259- ma.analyseStmt(f.getBody(), true);
276+ FlowBits flow2 = ma.analyseStmt(f.getBody(), true);
277+ flow |= flow2 & (FlowReturn | FlowGoto | FlowNoReturn | FlowError);
278+ if (flow2 & FlowBreak) flow |= FlowNext;
260279done:
261280 ma.scope.exit(ma.has_error);
281+ return flow;
262282}
263283
264- fn void Analyser.analyseWhileStmt(Analyser* ma, Stmt* s) {
284+ fn FlowBits Analyser.analyseWhileStmt(Analyser* ma, Stmt* s) {
285+ FlowBits flow = FlowNext;
265286 WhileStmt* w = (WhileStmt*)s;
287+
266288 ma.scope.enter(scope.Decl);
267289 ma.analyseCondition(w.getCond2(), true);
268290 if (ma.has_error) goto done;
269291
292+ Expr* cond = getCondExpr(w.getCond());
293+ if (cond.isCtv()) {
294+ Value v = ctv_analyser.get_value(cond);
295+ if (v.isZero()) {
296+ // TODO: body is never reached
297+ } else {
298+ flow = 0;
299+ }
300+ }
301+
270302 ma.scope.enter(scope.Break | scope.Continue | scope.Decl | scope.Control);
271- ma.analyseStmt(w.getBody(), true);
303+ FlowBits flow2 = ma.analyseStmt(w.getBody(), true);
272304 ma.scope.exit(ma.has_error);
305+ flow |= flow2 & (FlowReturn | FlowGoto | FlowNoReturn | FlowError);
306+ if (flow2 & FlowBreak) flow |= FlowNext;
273307done:
274308 ma.scope.exit(ma.has_error);
309+ return flow;
275310}
276311
277312fn QualType Analyser.analyseDeclStmt(Analyser* ma, Stmt* s) {
0 commit comments