class
compiler::CheckErrors
sys::Obj
compiler::CompilerSupport
compiler::CompilerStep
compiler::CheckErrors
1 //
2 // Copyright (c) 2006, Brian Frank and Andy Frank
3 // Licensed under the Academic Free License version 3.0
4 //
5 // History:
6 // 2 Dec 05 Brian Frank Creation
7 // 17 Sep 06 Brian Frank Ported from Java to Fan
8 //
9
10 **
11 ** CheckErrors walks the tree of statements and expressions looking
12 ** for errors the compiler can detect such as invalid type usage. We
13 ** attempt to leave all the error reporting to this step, so that we
14 ** can batch report as many errors as possible.
15 **
16 ** Since CheckErrors already performs a full tree walk down to each leaf
17 ** expression, we also do a couple of other AST decorations in this step:
18 ** 1) add temp local for field assignments like return ++x
19 ** 2) add temp local for returns inside protected region
20 ** 3) check for field accessor optimization
21 ** 4) check for field storage requirements
22 ** 5) add implicit cast when assigning Obj to non-Obj
23 **
24 class CheckErrors : CompilerStep
25 {
26
27 //////////////////////////////////////////////////////////////////////////
28 // Constructor
29 //////////////////////////////////////////////////////////////////////////
30
31 new make(Compiler compiler)
32 : super(compiler)
33 {
34 }
35
36 //////////////////////////////////////////////////////////////////////////
37 // Run
38 //////////////////////////////////////////////////////////////////////////
39
40 override Void run()
41 {
42 log.verbose("CheckErrors")
43 walk(types, VisitDepth.expr)
44 bombIfErr
45 }
46
47 //////////////////////////////////////////////////////////////////////////
48 // TypeDef
49 //////////////////////////////////////////////////////////////////////////
50
51 override Void visitTypeDef(TypeDef t)
52 {
53 // check type flags
54 checkTypeFlags(t)
55
56 // check for abstract slots in concrete class
57 checkAbstractSlots(t)
58
59 // check for const slots in const class
60 checkConstType(t)
61
62 // check some knuckle head doesn't override type
63 if (t.slotDef("type") != null && !compiler.isSys)
64 err("Cannot override Obj.type() knuckle head", t.slotDef("type").location)
65 }
66
67 private Void checkTypeFlags(TypeDef t)
68 {
69 flags := t.flags
70 loc := t.location
71
72 // these modifiers are never allowed on a type
73 if (flags & FConst.Ctor != 0) err("Cannot use 'new' modifier on type", loc)
74 if (flags & FConst.Native != 0) err("Cannot use 'native' modifier on type", loc)
75 if (flags & FConst.Override != 0) err("Cannot use 'override' modifier on type", loc)
76 if (flags & FConst.Private != 0) err("Cannot use 'private' modifier on type", loc)
77 if (flags & FConst.Protected != 0) err("Cannot use 'protected' modifier on type", loc)
78 if (flags & FConst.Static != 0) err("Cannot use 'static' modifier on type", loc)
79 if (flags & FConst.Virtual != 0) err("Cannot use 'virtual' modifier on type", loc)
80 if (flags & Parser.Readonly != 0) err("Cannot use 'readonly' modifier on type", loc)
81
82 // check invalid protection combinations
83 checkProtectionFlags(flags, loc)
84
85 // check abstract and final
86 if ((flags & FConst.Abstract != 0) && (flags & FConst.Final != 0))
87 err("Invalid combination of 'abstract' and 'final' modifiers", loc)
88 }
89
90 private Void checkAbstractSlots(TypeDef t)
91 {
92 // if already abstract, nothing to check
93 if (t.isAbstract) return
94
95 errForDef := false
96 closure := |CSlot slot|
97 {
98 if (!slot.isAbstract) return
99 if (slot.parent === t)
100 {
101 if (!errForDef)
102 {
103 err("Class '$t.name' must be abstract since it contains abstract slots", t.location)
104 errForDef = true
105 }
106 }
107 else
108 {
109 err("Class '$t.name' must be abstract since it inherits but doesn't override '$slot.qname'", t.location)
110 }
111 }
112
113 if (compiler.input.isTest)
114 t.slots.values.sort.each(closure)
115 else
116 t.slots.each(closure)
117 }
118
119 private Void checkConstType(TypeDef t)
120 {
121 // if not const, nothing to check
122 if (!t.isConst)
123 {
124 // non-const cannot inherit from const class
125 if (t.base != null && t.base.isConst)
126 err("Non-const class '$t.name' cannot subclass const class '$t.base'", t.location)
127 return
128 }
129
130 // const class cannot inherit from non-const class
131 if (t.base != null && t.base != ns.objType && !t.base.isConst)
132 err("Const class '$t.name' cannot subclass non-const class '$t.base'", t.location)
133
134 // check that each field is const (don't worry about statics
135 // because they are forced to be const in another check)
136 t.fieldDefs.each |FieldDef f|
137 {
138 if (!f.isConst && !f.isStatic)
139 err("Const class '$t.name' cannot contain non-const field '$f.name'", f.location)
140 }
141 }
142
143 //////////////////////////////////////////////////////////////////////////
144 // FieldDef
145 //////////////////////////////////////////////////////////////////////////
146
147 override Void visitFieldDef(FieldDef f)
148 {
149 // if this field overrides a concrete field,
150 // then it never gets its own storage
151 if (f.concreteBase != null)
152 f.flags &= ~FConst.Storage
153
154 // check for invalid flags
155 checkFieldFlags(f)
156
157 // mixins cannot have non-abstract fields
158 if (curType.isMixin && !f.isAbstract && !f.isStatic)
159 err("Mixin field '$f.name' must be abstract", f.location)
160
161 // abstract field cannot have initialization
162 if (f.isAbstract && f.init != null)
163 err("Abstract field '$f.name' cannot have initializer", f.init.location)
164
165 // abstract field cannot have getter/setter
166 if (f.isAbstract && (f.hasGet || f.hasSet))
167 err("Abstract field '$f.name' cannot have getter or setter", f.location)
168 }
169
170 private Void checkFieldFlags(FieldDef f)
171 {
172 flags := f.flags
173 loc := f.location
174
175 // these modifiers are never allowed on a field
176 if (flags & FConst.Ctor != 0) err("Cannot use 'new' modifier on field", loc)
177 if (flags & FConst.Final != 0) err("Cannot use 'final' modifier on field", loc)
178 if (flags & FConst.Native != 0) err("Cannot use 'native' modifier on field", loc)
179
180 // check invalid protection combinations
181 checkProtectionFlags(flags, loc)
182
183 // if const
184 if (flags & FConst.Const != 0)
185 {
186 // invalid const flag combo
187 if (flags & FConst.Abstract != 0) err("Invalid combination of 'const' and 'abstract' modifiers", loc)
188 else if (flags & FConst.Override != 0) err("Invalid combination of 'const' and 'override' modifiers", loc)
189 else if (flags & FConst.Virtual != 0) err("Invalid combination of 'const' and 'virtual' modifiers", loc)
190
191 // invalid type
192 if (!isConstFieldType(f.fieldType))
193 err("Const field '$f.name' has non-const type '$f.fieldType'", loc)
194 }
195 else
196 {
197 // static fields must be const
198 if (flags & FConst.Static != 0) err("Static field '$f.name' must be const", loc)
199 }
200
201 // check invalid protection combinations on setter (getter
202 // can no modifiers which is checked in the parser)
203 if (f.setter != null)
204 {
205 fieldProtection := flags & ~Parser.ProtectionMask
206 setterProtection := f.set.flags & ~Parser.ProtectionMask
207 if (fieldProtection != setterProtection)
208 {
209 // verify protection flag combinations
210 checkProtectionFlags(f.set.flags, loc)
211
212 // verify that setter has narrowed protection
213 if (fieldProtection & FConst.Private != 0)
214 {
215 if (setterProtection & FConst.Public != 0) err("Setter cannot have wider visibility than the field", loc)
216 if (setterProtection & FConst.Protected != 0) err("Setter cannot have wider visibility than the field", loc)
217 if (setterProtection & FConst.Internal != 0) err("Setter cannot have wider visibility than the field", loc)
218 }
219 else if (fieldProtection & FConst.Internal != 0)
220 {
221 if (setterProtection & FConst.Public != 0) err("Setter cannot have wider visibility than the field", loc)
222 if (setterProtection & FConst.Protected != 0) err("Setter cannot have wider visibility than the field", loc)
223 }
224 else if (fieldProtection & FConst.Protected != 0)
225 {
226 if (setterProtection & FConst.Public != 0) err("Setter cannot have wider visibility than the field", loc)
227 }
228 }
229 }
230 }
231
232 private Bool isConstFieldType(CType t)
233 {
234 if (t.isConst) return true
235 t = t.deref
236
237 if (t is ListType)
238 {
239 list := t as ListType
240 return isConstFieldType(list.v)
241 }
242
243 if (t is MapType)
244 {
245 map := t as MapType
246 return isConstFieldType(map.k) && isConstFieldType(map.v)
247 }
248
249 return false
250 }
251
252 //////////////////////////////////////////////////////////////////////////
253 // MethodDef
254 //////////////////////////////////////////////////////////////////////////
255
256 override Void visitMethodDef(MethodDef m)
257 {
258 // check invalid use of flags
259 checkMethodFlags(m)
260
261 // check parameters
262 checkParams(m)
263
264 // check ctors call super (or another this) ctor
265 if (m.isCtor()) checkCtor(m)
266 }
267
268 private Void checkMethodFlags(MethodDef m)
269 {
270 // check field accessors in checkFieldFlags
271 if (m.isFieldAccessor) return
272
273 flags := m.flags
274 loc := m.location
275
276 // these modifiers are never allowed on a method
277 if (flags & FConst.Final != 0) err("Cannot use 'final' modifier on method", loc)
278 if (flags & FConst.Const != 0) err("Cannot use 'const' modifier on method", loc)
279 if (flags & Parser.Readonly != 0) err("Cannot use 'readonly' modifier on method", loc)
280
281 // check invalid protection combinations
282 checkProtectionFlags(flags, loc)
283
284 // check invalid constructor flags
285 if (flags & FConst.Ctor != 0)
286 {
287 if (flags & FConst.Abstract != 0) err("Invalid combination of 'new' and 'abstract' modifiers", loc)
288 else if (flags & FConst.Override != 0) err("Invalid combination of 'new' and 'override' modifiers", loc)
289 else if (flags & FConst.Virtual != 0) err("Invalid combination of 'new' and 'virtual' modifiers", loc)
290 if (flags & FConst.Native != 0) err("Invalid combination of 'new' and 'native' modifiers", loc)
291 if (flags & FConst.Static != 0) err("Invalid combination of 'new' and 'static' modifiers", loc)
292 }
293
294 // check invalid static flags
295 if (flags & FConst.Static != 0)
296 {
297 if (flags & FConst.Abstract != 0) err("Invalid combination of 'static' and 'abstract' modifiers", loc)
298 else if (flags & FConst.Override != 0) err("Invalid combination of 'static' and 'override' modifiers", loc)
299 else if (flags & FConst.Virtual != 0) err("Invalid combination of 'static' and 'virtual' modifiers", loc)
300 }
301
302 // check invalid abstract flags
303 if (flags & FConst.Abstract != 0)
304 {
305 if (flags & FConst.Native != 0) err("Invalid combination of 'abstract' and 'native' modifiers", loc)
306 }
307
308 // normalize method flags after checking
309 if (m.flags & FConst.Static != 0)
310 m.flags |= FConst.Const;
311 }
312
313 private Void checkParams(MethodDef m)
314 {
315 // check that defs are contiguous after first one
316 seenDef := false
317 m.paramDefs.each |ParamDef p|
318 {
319 if (seenDef)
320 {
321 if (p.def == null)
322 err("Parameter '$p.name' must have default", p.location)
323 }
324 else
325 {
326 seenDef = p.def != null
327 }
328 }
329 }
330
331 private Void checkCtor(MethodDef m)
332 {
333 // mixins cannot have constructors
334 if (curType.isMixin)
335 err("Mixins cannot have constructors", m.location)
336
337 // ensure super/this constructor is called
338 if (m.ctorChain == null && !compiler.isSys && !curType.base.isObj && !curType.isSynthetic)
339 err("Must call super class constructor in '$m.name'", m.location)
340 }
341
342 //////////////////////////////////////////////////////////////////////////
343 // Statements
344 //////////////////////////////////////////////////////////////////////////
345
346 override Void enterStmt(Stmt stmt)
347 {
348 if (stmt.id == StmtId.tryStmt) protectedRegionDepth++
349 }
350
351 override Void exitStmt(Stmt stmt)
352 {
353 if (stmt.id == StmtId.tryStmt) protectedRegionDepth--
354 }
355
356 override Void enterFinally(TryStmt stmt)
357 {
358 finallyDepth++
359 }
360
361 override Void exitFinally(TryStmt stmt)
362 {
363 finallyDepth--
364 }
365
366 override Void visitStmt(Stmt stmt)
367 {
368 switch (stmt.id)
369 {
370 case StmtId.expr: checkExprStmt((ExprStmt)stmt)
371 case StmtId.ifStmt: checkIf((IfStmt)stmt)
372 case StmtId.returnStmt: checkReturn((ReturnStmt)stmt)
373 case StmtId.throwStmt: checkThrow((ThrowStmt)stmt)
374 case StmtId.forStmt: checkFor((ForStmt)stmt)
375 case StmtId.whileStmt: checkWhile((WhileStmt)stmt)
376 case StmtId.breakStmt: checkBreak((BreakStmt)stmt)
377 case StmtId.continueStmt: checkContinue((ContinueStmt)stmt)
378 case StmtId.tryStmt: checkTry((TryStmt)stmt)
379 case StmtId.switchStmt: checkSwitch((SwitchStmt)stmt)
380 }
381 }
382
383 private Void checkExprStmt(ExprStmt stmt)
384 {
385 if (!stmt.expr.isStmt)
386 err("Not a statement", stmt.expr.location)
387 }
388
389 private Void checkIf(IfStmt stmt)
390 {
391 if (!stmt.condition.ctype.isBool)
392 {
393 if (stmt.condition.ctype.isObj)
394 stmt.condition = cast(stmt.condition, ns.boolType)
395 else
396 err("If condition must be Bool, not '$stmt.condition.ctype'", stmt.condition.location)
397 }
398 }
399
400 private Void checkThrow(ThrowStmt stmt)
401 {
402 if (!stmt.exception.fits(ns.errType))
403 {
404 if (stmt.exception.ctype.isObj)
405 stmt.exception = cast(stmt.exception, ns.errType)
406 else
407 err("Must throw Err, not '$stmt.exception.ctype'", stmt.exception.location)
408 }
409 }
410
411 private Void checkFor(ForStmt stmt)
412 {
413 if (stmt.condition != null && !stmt.condition.ctype.isBool)
414 {
415 if (stmt.condition.ctype.isObj)
416 stmt.condition = cast(stmt.condition, ns.boolType)
417 else
418 err("For condition must be Bool, not '$stmt.condition.ctype'", stmt.condition.location)
419 }
420 }
421
422 private Void checkWhile(WhileStmt stmt)
423 {
424 if (!stmt.condition.ctype.isBool)
425 {
426 if (stmt.condition.ctype.isObj)
427 stmt.condition = cast(stmt.condition, ns.boolType)
428 else
429 err("While condition must be Bool, not '$stmt.condition.ctype'", stmt.condition.location)
430 }
431 }
432
433 private Void checkBreak(BreakStmt stmt)
434 {
435 if (stmt.loop == null)
436 err("Break outside of loop (break is implicit in switch)", stmt.location)
437
438 // can't leave control of a finally block
439 if (finallyDepth > 0)
440 err("Cannot leave finally block", stmt.location)
441 }
442
443 private Void checkContinue(ContinueStmt stmt)
444 {
445 if (stmt.loop == null)
446 err("Continue outside of loop", stmt.location)
447
448 // can't leave control of a finally block
449 if (finallyDepth > 0)
450 err("Cannot leave finally block", stmt.location)
451 }
452
453 private Void checkReturn(ReturnStmt stmt)
454 {
455 ret := curMethod.ret
456 if (stmt.expr == null)
457 {
458 // this is just a sanity check - it should be caught in parser
459 if (!ret.isVoid)
460 err("Must return a value from non-Void method", stmt.location)
461 }
462 else
463 {
464 if (!stmt.expr.fits(ret))
465 {
466 if (stmt.expr.ctype.isObj)
467 stmt.expr = cast(stmt.expr, ret)
468 else
469 err("Cannot return '$stmt.expr.ctype' as '$ret'", stmt.expr.location)
470 }
471 }
472
473 // can't leave control of a finally block
474 if (finallyDepth > 0)
475 err("Cannot leave finally block", stmt.location)
476
477 // add temp local var if returning from a protected region,
478 // we always call this variable "$return" and reuse it if
479 // already declared by a previous return
480 if (stmt.expr != null && protectedRegionDepth > 0)
481 {
482 v := curMethod.vars.find |MethodVar v->Bool| { return v.name == "\$return" }
483 if (v == null) v = curMethod.addLocalVar(stmt.expr.ctype, "\$return", null)
484 stmt.leaveVar = v
485 }
486 }
487
488 private Void checkTry(TryStmt stmt)
489 {
490 caught := CType[,]
491 stmt.catches.each |Catch c|
492 {
493 CType errType := c.errType
494 if (errType == null) errType = ns.errType
495 if (!errType.fits(ns.errType))
496 err("Must catch Err, not '$c.errType'", c.errType.location)
497 else if (errType.fitsAny(caught))
498 err("Already caught '$errType'", c.location)
499 caught.add(errType)
500 }
501 }
502
503 private Void checkSwitch(SwitchStmt stmt)
504 {
505 dups := Int:Int[:]
506
507 stmt.cases.each |Case c|
508 {
509 for (i:=0; i<c.cases.size; ++i)
510 {
511 expr := c.cases[i]
512
513 // check comparability of condition and each case
514 checkCompare(expr, stmt.condition)
515
516 // check for dups
517 literal := expr.asTableSwitchCase
518 if (literal != null)
519 {
520 if (dups[literal] == null)
521 dups[literal] = literal
522 else
523 err("Duplicate case label", expr.location)
524 }
525 }
526 }
527 }
528
529 //////////////////////////////////////////////////////////////////////////
530 // Expr
531 //////////////////////////////////////////////////////////////////////////
532
533 override Expr visitExpr(Expr expr)
534 {
535 switch (expr.id)
536 {
537 case ExprId.rangeLiteral: checkRangeLiteral((RangeLiteralExpr)expr)
538 case ExprId.simpleLiteral: checkSimpleLiteral((SimpleLiteralExpr)expr)
539 case ExprId.boolNot: checkBool((UnaryExpr)expr)
540 case ExprId.assign: checkAssign((BinaryExpr)expr)
541 case ExprId.boolOr:
542 case ExprId.boolAnd: checkBools((CondExpr)expr)
543 case ExprId.same:
544 case ExprId.notSame: checkSame((BinaryExpr)expr)
545 case ExprId.shortcut: checkShortcut((ShortcutExpr)expr)
546 case ExprId.call: checkCall((CallExpr)expr)
547 case ExprId.field: checkField((FieldExpr)expr)
548 case ExprId.thisExpr: checkThis((ThisExpr)expr)
549 case ExprId.superExpr: checkSuper((SuperExpr)expr)
550 case ExprId.isExpr:
551 case ExprId.asExpr:
552 case ExprId.cast: checkTypeCheck((TypeCheckExpr)expr)
553 case ExprId.ternary: checkTernary((TernaryExpr)expr)
554 case ExprId.withBlock: checkWithBlock((WithBlockExpr)expr)
555 }
556 return expr
557 }
558
559 private Void checkRangeLiteral(RangeLiteralExpr range)
560 {
561 if (!range.start.ctype.isInt || !range.end.ctype.isInt)
562 err("Range must be Int..Int, not '${range.start.ctype}..${range.end.ctype}'", range.location)
563 }
564
565 private Void checkSimpleLiteral(SimpleLiteralExpr simple)
566 {
567 t := simple.ctype
568 m := simple.method = t.method("fromStr")
569
570 // resolve fromStr method
571 if (m == null)
572 {
573 err("Simple type '$t' missing 'fromStr' method", simple.location)
574 return
575 }
576
577 // check fromStr is static
578 if (!m.isStatic)
579 err("Simple type '${t}.fromStr' not static method", simple.location)
580
581 // check fromStr return
582 if (m.returnType != t)
583 err("Simple type '${t}.fromStr' returns wrong type", simple.location)
584
585 // check fromStr parameters
586 if (m.params.size < 1)
587 {
588 err("Simple type '${t}.fromStr' not enough parameters", simple.location)
589 }
590 else
591 {
592 if (!m.params[0].paramType.isStr)
593 err("Simple type '${t}.fromStr' first parameter not Str", simple.location)
594 if (m.params.size > 1 && !m.params[1].hasDefault)
595 err("Simple type '${t}.fromStr' too many parameters", simple.location)
596 }
597
598 // check argument is a string
599 argType := simple.arg.ctype
600 if (!argType.isStr)
601 err("Simple literal requires 'Str' argument, not '$argType'", simple.arg.location)
602 }
603
604 private Void checkBool(UnaryExpr expr)
605 {
606 operand := expr.operand.ctype
607 if (!operand.isBool)
608 {
609 if (operand.isObj)
610 expr.operand = cast(expr.operand, ns.boolType)
611 else
612 err("Cannot apply '$expr.opToken.symbol' operator to '$operand'", expr.location)
613 }
614 }
615
616 private Void checkBools(CondExpr expr)
617 {
618 expr.operands.each |Expr operand, Int i|
619 {
620 if (!operand.ctype.isBool)
621 {
622 if (operand.ctype.isObj)
623 expr.operands[i] = cast(operand, ns.boolType)
624 else
625 err("Cannot apply '$expr.opToken.symbol' operator to '$operand.ctype'", operand.location)
626 }
627 }
628 }
629
630 private Void checkSame(BinaryExpr expr)
631 {
632 checkCompare(expr.lhs, expr.rhs)
633 }
634
635 private Void checkCompare(Expr lhs, Expr rhs)
636 {
637 if (!lhs.fits(rhs.ctype) && !rhs.fits(lhs.ctype))
638 err("Incomparable types '$lhs.ctype' and '$rhs.ctype'", lhs.location)
639 }
640
641 private Void checkAssign(BinaryExpr expr)
642 {
643 // check that rhs is assignable to lhs
644 if (!expr.rhs.fits(expr.lhs.ctype))
645 {
646 if (expr.rhs.ctype.isObj)
647 expr.rhs = cast(expr.rhs, expr.lhs.ctype)
648 else
649 err("'$expr.rhs.ctype' is not assignable to '$expr.lhs.ctype'", expr.rhs.location)
650 }
651
652 // check that lhs is assignable
653 if (!expr.lhs.isAssignable)
654 err("Left hand side is not assignable", expr.lhs.location)
655
656 // check left hand side field (common code with checkShortcut)
657 if (expr.lhs.id === ExprId.field)
658 checkAssignField((FieldExpr)expr.lhs, expr.rhs)
659
660 // take this opportunity to generate a temp local variable if needed
661 if (expr.leave && expr.lhs.assignRequiresTempVar)
662 expr.tempVar = curMethod.addLocalVar(expr.ctype, null, null)
663 }
664
665 private Void checkShortcut(ShortcutExpr shortcut)
666 {
667 switch (shortcut.opToken)
668 {
669 // comparable
670 case Token.eq:
671 case Token.notEq:
672 checkCompare(shortcut.target, shortcut.args.first)
673 }
674
675 // if assignment
676 if (shortcut.isAssign)
677 {
678 // check that lhs is assignable
679 if (!shortcut.target.isAssignable)
680 err("Target is not assignable", shortcut.target.location)
681
682 // check left hand side field (common code with checkAssign)
683 if (shortcut.target.id === ExprId.field)
684 checkAssignField((FieldExpr)shortcut.target, shortcut.args.first)
685 }
686
687 // take this oppotunity to generate a temp local variable if needed
688 if (shortcut.leave && shortcut.isAssign && shortcut.target.assignRequiresTempVar)
689 shortcut.tempVar = curMethod.addLocalVar(shortcut.ctype, null, null)
690
691 // perform normal call checking
692 checkCall(shortcut)
693 }
694
695 private Void checkAssignField(FieldExpr lhs, Expr rhs)
696 {
697 field := ((FieldExpr)lhs).field
698
699 // check protection scope (which might be more narrow than the scope
700 // of the entire field as checked in checkProtection by checkField)
701 if (field.setter != null && slotProtectionErr(field) == null)
702 checkSlotProtection(field.setter, lhs.location, true)
703
704 // if not-const we are done
705 if (!field.isConst) return
706
707 // check attempt to set field outside of owning class
708 if (field.parent != curType)
709 {
710 err("Cannot set const field '$field.qname'", lhs.location)
711 return
712 }
713
714 // check attempt to set static field outside of static initializer
715 if (field.isStatic && !curMethod.isStaticInit)
716 {
717 err("Cannot set const static field '$field.name' outside of static initializer", lhs.location)
718 return
719 }
720
721 // check attempt to set instance field outside of ctor
722 if (!field.isStatic && !(curMethod.isInstanceInit || curMethod.isCtor))
723 {
724 err("Cannot set const field '$field.name' outside of constructor", lhs.location)
725 return
726 }
727
728 // if collection, verify that rhs assignment is call to toImmutable()
729 ftype := field.fieldType
730 if (ftype.fits(ns.listType) || ftype.fits(ns.mapType) && rhs != null)
731 {
732 if (rhs.id != ExprId.call || rhs->method->qname != "sys::${ftype.name}.toImmutable")
733 err("Must call ${ftype.name}.toImmutable() when setting const field '$field.name'", rhs.location)
734 }
735 }
736
737 private Void checkCall(CallExpr call)
738 {
739 m := call.method
740 if (m == null)
741 {
742 err("Something wrong with method call?", call.location)
743 return
744 }
745
746 name := m.name
747
748 // check protection scope
749 checkSlotProtection(call.method, call.location)
750
751 // check arguments
752 if (!call.isDynamic) checkArgs(call)
753
754 // if constructor
755 if (m.isCtor && !call.isCtorChain)
756 {
757 // ensure we aren't calling constructors on an instance
758 if (call.target != null && call.target.id !== ExprId.staticTarget)
759 err("Cannot call constructor '$name' on instance", call.location)
760
761 // ensure we aren't calling a constructor on an abstract class
762 if (m.parent.isAbstract)
763 err("Calling constructor on abstract class", call.location)
764 }
765
766 // ensure we aren't calling static methods on an instance
767 if (m.isStatic)
768 {
769 if (call.target != null && call.target.id !== ExprId.staticTarget)
770 err("Cannot call static method '$name' on instance", call.location)
771 }
772
773 // ensure we can't calling an instance method statically
774 if (!m.isStatic && !m.isCtor)
775 {
776 if (call.target == null || call.target.id === ExprId.staticTarget)
777 err("Cannot call instance method '$name' in static context", call.location)
778 }
779
780 // if using super check that concrete
781 if (call.target != null && call.target.id === ExprId.superExpr)
782 {
783 if (m.isAbstract)
784 err("Cannot use super to call abstract method '$m.qname'", call.target.location)
785 }
786 }
787
788 private Void checkField(FieldExpr f)
789 {
790 field := f.field
791
792 // check protection scope
793 checkSlotProtection(field, f.location)
794
795 // ensure we aren't calling static methods on an instance
796 if (field.isStatic)
797 {
798 if (f.target != null && f.target.id !== ExprId.staticTarget)
799 err("Cannot access static field '$f.name' on instance", f.location)
800 }
801
802 // if instance field
803 else
804 {
805 if (f.target == null || f.target.id === ExprId.staticTarget)
806 err("Cannot access instance field '$f.name' in static context", f.location)
807 }
808
809 // if using super check that concrete
810 if (f.target != null && f.target.id === ExprId.superExpr)
811 {
812 if (field.isAbstract)
813 err("Cannot use super to access abstract field '$field.qname'", f.target.location)
814 }
815
816 // if using the field's accessor method
817 if (f.useAccessor)
818 {
819 // check if we can optimize out the accessor (required for constants)
820 f.useAccessor = useFieldAccessor(field)
821
822 // check that we aren't using an field accessor inside of itself
823 if (curMethod != null && (field.getter === curMethod || field.setter === curMethod))
824 err("Cannot use field accessor inside accessor itself - use '@' operator", f.location)
825 }
826
827 // if accessing storage directly
828 else
829 {
830 // check that the current class gets access to direct
831 // field storage (only defining class gets it); allow closures
832 // same scope priviledges as enclosing class
833 enclosing := curType.isClosure ? curType.closure.enclosingType : curType
834 if (!field.isConst && field.parent != curType && field.parent != enclosing)
835 {
836 err("Field storage for '$field.qname' not accessible", f.location)
837 }
838
839 // sanity check that field has storage
840 else if (!field.isStorage)
841 {
842 if (field is FieldDef && ((FieldDef)field).concreteBase != null)
843 err("Field storage of inherited field '${field->concreteBase->qname}' not accessible (might try super)", f.location)
844 else
845 err("Invalid storage access of field '$field.qname' which doesn't have storage", f.location)
846 }
847 }
848 }
849
850 private Bool useFieldAccessor(CField f)
851 {
852 // if there is no getter, then use field directly (constants)
853 if (f.getter == null) return false
854
855 // always use accessor if field is imported from another
856 // pod (in which case it isn't a def in my compilation unit)
857 def := f as FieldDef
858 if (def == null) return true
859
860 // if virtual and/or override then always use accessor
861 if (def.isVirtual || def.isOverride)
862 return true
863
864 // if the field has synthetic getter and setter, then
865 // we can safely optimize internal field accessors to
866 // use field directly
867 if (!def.hasGet && !def.hasSet)
868 return false
869
870 // use accessor since there is a custom getter or setter
871 return true
872 }
873
874 private Void checkThis(ThisExpr expr)
875 {
876 if (inStatic)
877 err("Cannot access 'this' in static context", expr.location)
878 }
879
880 private Void checkSuper(SuperExpr expr)
881 {
882 if (inStatic)
883 err("Cannot access 'super' in static context", expr.location)
884
885 if (curType.isMixin)
886 {
887 if (expr.explicitType == null)
888 err("Must use named 'super' inside mixin", expr.location)
889 else if (!expr.explicitType.isMixin)
890 err("Cannot use 'Obj.super' inside mixin (yeah I know - take it up with Sun)", expr.location)
891 }
892
893 if (expr.explicitType != null)
894 {
895 if (!curType.fits(expr.explicitType))
896 err("Named super '$expr.explicitType' not a super class of '$curType.name'", expr.location)
897 }
898 }
899
900 private Void checkTypeCheck(TypeCheckExpr expr)
901 {
902 check := expr.check
903 target := expr.target.ctype
904 if (!check.fits(target) && !target.fits(check))
905 err("Inconvertible types '$target' and '$check'", expr.location)
906 }
907
908 private Void checkTernary(TernaryExpr expr)
909 {
910 if (!expr.condition.ctype.isBool)
911 {
912 if (expr.condition.ctype.isObj)
913 expr.condition = cast(expr.condition, ns.boolType)
914 else
915 err("Ternary condition must be Bool, not '$expr.condition.ctype'", expr.condition.location)
916 }
917 }
918
919 private Void checkWithBlock(WithBlockExpr expr)
920 {
921 expr.subs.each |Expr sub|
922 {
923 if (!sub.isStmt) err("Not a statement", sub.location)
924 }
925 }
926
927 //////////////////////////////////////////////////////////////////////////
928 // Check Args
929 //////////////////////////////////////////////////////////////////////////
930
931 private Void checkArgs(CallExpr call)
932 {
933 params := call.method.params
934 name := call.name
935 args := call.args
936 isErr := false
937
938 // if we are calling callx(A, B...) on a FuncType, then
939 // use the first class Func signature rather than the
940 // version of callx which got picked because we might have
941 // picked the wrong callx version
942 sig := call.method.parent as FuncType
943 if (sig != null && name.startsWith("call") && name.size == 5)
944 {
945 if (sig.params.size != args.size)
946 {
947 isErr = true
948 }
949 else
950 {
951 sig.params.each |CType p, Int i|
952 {
953 arg := args[i]
954 if (!arg.fits(p))
955 {
956 if (arg.ctype.isObj)
957 args[i] = cast(arg, p)
958 else
959 isErr = true
960 }
961 }
962 }
963 }
964
965 // if more args than params, always an err
966 else if (params.size < args.size)
967 {
968 isErr = true
969 }
970
971 // check each arg against each parameter
972 else
973 {
974 params.each |CParam p, Int i|
975 {
976 if (i >= args.size)
977 {
978 // param has a default value, then that is ok
979 if (!p.hasDefault) isErr = true
980 }
981 else
982 {
983 // ensure arg fits parameter type (or auto-cast)
984 arg := args[i]
985 if (!arg.fits(p.paramType))
986 {
987 if (arg.ctype.isObj)
988 args[i] = cast(arg, p.paramType)
989 else
990 isErr = true
991 }
992 }
993 }
994 }
995
996 if (!isErr) return
997
998 msg := "Invalid args "
999 if (sig != null)
1000 msg += "|" + sig.params.join(", ") + "|"
1001 else
1002 msg += call.method.nameAndParamTypesToStr
1003 msg += ", not (" + args.join(", ", |Expr e->Str| { return "$e.ctype" }) + ")"
1004 err(msg, call.location)
1005 }
1006
1007 //////////////////////////////////////////////////////////////////////////
1008 // Flag Utils
1009 //////////////////////////////////////////////////////////////////////////
1010
1011 private Void checkProtectionFlags(Int flags, Location loc)
1012 {
1013 isPublic := flags & FConst.Public != 0
1014 isProtected := flags & FConst.Protected != 0
1015 isPrivate := flags & FConst.Private != 0
1016 isInternal := flags & FConst.Internal != 0
1017 isVirtual := flags & FConst.Virtual != 0
1018 isOverride := flags & FConst.Override != 0
1019
1020 if (isPublic)
1021 {
1022 if (isProtected) err("Invalid combination of 'public' and 'protected' modifiers", loc)
1023 if (isPrivate) err("Invalid combination of 'public' and 'private' modifiers", loc)
1024 if (isInternal) err("Invalid combination of 'public' and 'internal' modifiers", loc)
1025 }
1026 else if (isProtected)
1027 {
1028 if (isPrivate) err("Invalid combination of 'protected' and 'private' modifiers", loc)
1029 if (isInternal) err("Invalid combination of 'protected' and 'internal' modifiers", loc)
1030 }
1031 else if (isPrivate)
1032 {
1033 if (isInternal) err("Invalid combination of 'private' and 'internal' modifiers", loc)
1034 if (isVirtual && !isOverride) err("Invalid combination of 'private' and 'virtual' modifiers", loc)
1035 }
1036 }
1037
1038 private Void checkSlotProtection(CSlot slot, Location loc, Bool setter := false)
1039 {
1040 errMsg := slotProtectionErr(slot, setter)
1041 if (errMsg != null) err(errMsg, loc)
1042 }
1043
1044 private Str slotProtectionErr(CSlot slot, Bool setter := false)
1045 {
1046 msg := setter ? "setter of field" : (slot is CMethod ? "method" : "field")
1047
1048 // short circuit if method on myself
1049 if (curType == slot.parent)
1050 return null
1051
1052 // allow closures same scope priviledges as enclosing class
1053 myType := curType
1054 if (myType.isClosure)
1055 myType = curType.closure.enclosingType
1056
1057 if (slot.isPrivate && myType != slot.parent)
1058 return "Private $msg '$slot.qname' not accessible"
1059
1060 else if (slot.isProtected && !myType.fits(slot.parent))
1061 return "Protected $msg '$slot.qname' not accessible"
1062
1063 else if (slot.isInternal && myType.pod != slot.parent.pod)
1064 return "Internal $msg '$slot.qname' not accessible"
1065
1066 else
1067 return null
1068 }
1069
1070 //////////////////////////////////////////////////////////////////////////
1071 // Utils
1072 //////////////////////////////////////////////////////////////////////////
1073
1074 private static Expr cast(Expr target, CType to)
1075 {
1076 return TypeCheckExpr.cast(target, to)
1077 }
1078
1079 //////////////////////////////////////////////////////////////////////////
1080 // Fields
1081 //////////////////////////////////////////////////////////////////////////
1082
1083 private Int protectedRegionDepth := 0 // try statement depth
1084 private Int finallyDepth := 0 // finally block depth
1085 }
2 // Copyright (c) 2006, Brian Frank and Andy Frank
3 // Licensed under the Academic Free License version 3.0
4 //
5 // History:
6 // 2 Dec 05 Brian Frank Creation
7 // 17 Sep 06 Brian Frank Ported from Java to Fan
8 //
9
10 **
11 ** CheckErrors walks the tree of statements and expressions looking
12 ** for errors the compiler can detect such as invalid type usage. We
13 ** attempt to leave all the error reporting to this step, so that we
14 ** can batch report as many errors as possible.
15 **
16 ** Since CheckErrors already performs a full tree walk down to each leaf
17 ** expression, we also do a couple of other AST decorations in this step:
18 ** 1) add temp local for field assignments like return ++x
19 ** 2) add temp local for returns inside protected region
20 ** 3) check for field accessor optimization
21 ** 4) check for field storage requirements
22 ** 5) add implicit cast when assigning Obj to non-Obj
23 **
24 class CheckErrors : CompilerStep
25 {
26
27 //////////////////////////////////////////////////////////////////////////
28 // Constructor
29 //////////////////////////////////////////////////////////////////////////
30
31 new make(Compiler compiler)
32 : super(compiler)
33 {
34 }
35
36 //////////////////////////////////////////////////////////////////////////
37 // Run
38 //////////////////////////////////////////////////////////////////////////
39
40 override Void run()
41 {
42 log.verbose("CheckErrors")
43 walk(types, VisitDepth.expr)
44 bombIfErr
45 }
46
47 //////////////////////////////////////////////////////////////////////////
48 // TypeDef
49 //////////////////////////////////////////////////////////////////////////
50
51 override Void visitTypeDef(TypeDef t)
52 {
53 // check type flags
54 checkTypeFlags(t)
55
56 // check for abstract slots in concrete class
57 checkAbstractSlots(t)
58
59 // check for const slots in const class
60 checkConstType(t)
61
62 // check some knuckle head doesn't override type
63 if (t.slotDef("type") != null && !compiler.isSys)
64 err("Cannot override Obj.type() knuckle head", t.slotDef("type").location)
65 }
66
67 private Void checkTypeFlags(TypeDef t)
68 {
69 flags := t.flags
70 loc := t.location
71
72 // these modifiers are never allowed on a type
73 if (flags & FConst.Ctor != 0) err("Cannot use 'new' modifier on type", loc)
74 if (flags & FConst.Native != 0) err("Cannot use 'native' modifier on type", loc)
75 if (flags & FConst.Override != 0) err("Cannot use 'override' modifier on type", loc)
76 if (flags & FConst.Private != 0) err("Cannot use 'private' modifier on type", loc)
77 if (flags & FConst.Protected != 0) err("Cannot use 'protected' modifier on type", loc)
78 if (flags & FConst.Static != 0) err("Cannot use 'static' modifier on type", loc)
79 if (flags & FConst.Virtual != 0) err("Cannot use 'virtual' modifier on type", loc)
80 if (flags & Parser.Readonly != 0) err("Cannot use 'readonly' modifier on type", loc)
81
82 // check invalid protection combinations
83 checkProtectionFlags(flags, loc)
84
85 // check abstract and final
86 if ((flags & FConst.Abstract != 0) && (flags & FConst.Final != 0))
87 err("Invalid combination of 'abstract' and 'final' modifiers", loc)
88 }
89
90 private Void checkAbstractSlots(TypeDef t)
91 {
92 // if already abstract, nothing to check
93 if (t.isAbstract) return
94
95 errForDef := false
96 closure := |CSlot slot|
97 {
98 if (!slot.isAbstract) return
99 if (slot.parent === t)
100 {
101 if (!errForDef)
102 {
103 err("Class '$t.name' must be abstract since it contains abstract slots", t.location)
104 errForDef = true
105 }
106 }
107 else
108 {
109 err("Class '$t.name' must be abstract since it inherits but doesn't override '$slot.qname'", t.location)
110 }
111 }
112
113 if (compiler.input.isTest)
114 t.slots.values.sort.each(closure)
115 else
116 t.slots.each(closure)
117 }
118
119 private Void checkConstType(TypeDef t)
120 {
121 // if not const, nothing to check
122 if (!t.isConst)
123 {
124 // non-const cannot inherit from const class
125 if (t.base != null && t.base.isConst)
126 err("Non-const class '$t.name' cannot subclass const class '$t.base'", t.location)
127 return
128 }
129
130 // const class cannot inherit from non-const class
131 if (t.base != null && t.base != ns.objType && !t.base.isConst)
132 err("Const class '$t.name' cannot subclass non-const class '$t.base'", t.location)
133
134 // check that each field is const (don't worry about statics
135 // because they are forced to be const in another check)
136 t.fieldDefs.each |FieldDef f|
137 {
138 if (!f.isConst && !f.isStatic)
139 err("Const class '$t.name' cannot contain non-const field '$f.name'", f.location)
140 }
141 }
142
143 //////////////////////////////////////////////////////////////////////////
144 // FieldDef
145 //////////////////////////////////////////////////////////////////////////
146
147 override Void visitFieldDef(FieldDef f)
148 {
149 // if this field overrides a concrete field,
150 // then it never gets its own storage
151 if (f.concreteBase != null)
152 f.flags &= ~FConst.Storage
153
154 // check for invalid flags
155 checkFieldFlags(f)
156
157 // mixins cannot have non-abstract fields
158 if (curType.isMixin && !f.isAbstract && !f.isStatic)
159 err("Mixin field '$f.name' must be abstract", f.location)
160
161 // abstract field cannot have initialization
162 if (f.isAbstract && f.init != null)
163 err("Abstract field '$f.name' cannot have initializer", f.init.location)
164
165 // abstract field cannot have getter/setter
166 if (f.isAbstract && (f.hasGet || f.hasSet))
167 err("Abstract field '$f.name' cannot have getter or setter", f.location)
168 }
169
170 private Void checkFieldFlags(FieldDef f)
171 {
172 flags := f.flags
173 loc := f.location
174
175 // these modifiers are never allowed on a field
176 if (flags & FConst.Ctor != 0) err("Cannot use 'new' modifier on field", loc)
177 if (flags & FConst.Final != 0) err("Cannot use 'final' modifier on field", loc)
178 if (flags & FConst.Native != 0) err("Cannot use 'native' modifier on field", loc)
179
180 // check invalid protection combinations
181 checkProtectionFlags(flags, loc)
182
183 // if const
184 if (flags & FConst.Const != 0)
185 {
186 // invalid const flag combo
187 if (flags & FConst.Abstract != 0) err("Invalid combination of 'const' and 'abstract' modifiers", loc)
188 else if (flags & FConst.Override != 0) err("Invalid combination of 'const' and 'override' modifiers", loc)
189 else if (flags & FConst.Virtual != 0) err("Invalid combination of 'const' and 'virtual' modifiers", loc)
190
191 // invalid type
192 if (!isConstFieldType(f.fieldType))
193 err("Const field '$f.name' has non-const type '$f.fieldType'", loc)
194 }
195 else
196 {
197 // static fields must be const
198 if (flags & FConst.Static != 0) err("Static field '$f.name' must be const", loc)
199 }
200
201 // check invalid protection combinations on setter (getter
202 // can no modifiers which is checked in the parser)
203 if (f.setter != null)
204 {
205 fieldProtection := flags & ~Parser.ProtectionMask
206 setterProtection := f.set.flags & ~Parser.ProtectionMask
207 if (fieldProtection != setterProtection)
208 {
209 // verify protection flag combinations
210 checkProtectionFlags(f.set.flags, loc)
211
212 // verify that setter has narrowed protection
213 if (fieldProtection & FConst.Private != 0)
214 {
215 if (setterProtection & FConst.Public != 0) err("Setter cannot have wider visibility than the field", loc)
216 if (setterProtection & FConst.Protected != 0) err("Setter cannot have wider visibility than the field", loc)
217 if (setterProtection & FConst.Internal != 0) err("Setter cannot have wider visibility than the field", loc)
218 }
219 else if (fieldProtection & FConst.Internal != 0)
220 {
221 if (setterProtection & FConst.Public != 0) err("Setter cannot have wider visibility than the field", loc)
222 if (setterProtection & FConst.Protected != 0) err("Setter cannot have wider visibility than the field", loc)
223 }
224 else if (fieldProtection & FConst.Protected != 0)
225 {
226 if (setterProtection & FConst.Public != 0) err("Setter cannot have wider visibility than the field", loc)
227 }
228 }
229 }
230 }
231
232 private Bool isConstFieldType(CType t)
233 {
234 if (t.isConst) return true
235 t = t.deref
236
237 if (t is ListType)
238 {
239 list := t as ListType
240 return isConstFieldType(list.v)
241 }
242
243 if (t is MapType)
244 {
245 map := t as MapType
246 return isConstFieldType(map.k) && isConstFieldType(map.v)
247 }
248
249 return false
250 }
251
252 //////////////////////////////////////////////////////////////////////////
253 // MethodDef
254 //////////////////////////////////////////////////////////////////////////
255
256 override Void visitMethodDef(MethodDef m)
257 {
258 // check invalid use of flags
259 checkMethodFlags(m)
260
261 // check parameters
262 checkParams(m)
263
264 // check ctors call super (or another this) ctor
265 if (m.isCtor()) checkCtor(m)
266 }
267
268 private Void checkMethodFlags(MethodDef m)
269 {
270 // check field accessors in checkFieldFlags
271 if (m.isFieldAccessor) return
272
273 flags := m.flags
274 loc := m.location
275
276 // these modifiers are never allowed on a method
277 if (flags & FConst.Final != 0) err("Cannot use 'final' modifier on method", loc)
278 if (flags & FConst.Const != 0) err("Cannot use 'const' modifier on method", loc)
279 if (flags & Parser.Readonly != 0) err("Cannot use 'readonly' modifier on method", loc)
280
281 // check invalid protection combinations
282 checkProtectionFlags(flags, loc)
283
284 // check invalid constructor flags
285 if (flags & FConst.Ctor != 0)
286 {
287 if (flags & FConst.Abstract != 0) err("Invalid combination of 'new' and 'abstract' modifiers", loc)
288 else if (flags & FConst.Override != 0) err("Invalid combination of 'new' and 'override' modifiers", loc)
289 else if (flags & FConst.Virtual != 0) err("Invalid combination of 'new' and 'virtual' modifiers", loc)
290 if (flags & FConst.Native != 0) err("Invalid combination of 'new' and 'native' modifiers", loc)
291 if (flags & FConst.Static != 0) err("Invalid combination of 'new' and 'static' modifiers", loc)
292 }
293
294 // check invalid static flags
295 if (flags & FConst.Static != 0)
296 {
297 if (flags & FConst.Abstract != 0) err("Invalid combination of 'static' and 'abstract' modifiers", loc)
298 else if (flags & FConst.Override != 0) err("Invalid combination of 'static' and 'override' modifiers", loc)
299 else if (flags & FConst.Virtual != 0) err("Invalid combination of 'static' and 'virtual' modifiers", loc)
300 }
301
302 // check invalid abstract flags
303 if (flags & FConst.Abstract != 0)
304 {
305 if (flags & FConst.Native != 0) err("Invalid combination of 'abstract' and 'native' modifiers", loc)
306 }
307
308 // normalize method flags after checking
309 if (m.flags & FConst.Static != 0)
310 m.flags |= FConst.Const;
311 }
312
313 private Void checkParams(MethodDef m)
314 {
315 // check that defs are contiguous after first one
316 seenDef := false
317 m.paramDefs.each |ParamDef p|
318 {
319 if (seenDef)
320 {
321 if (p.def == null)
322 err("Parameter '$p.name' must have default", p.location)
323 }
324 else
325 {
326 seenDef = p.def != null
327 }
328 }
329 }
330
331 private Void checkCtor(MethodDef m)
332 {
333 // mixins cannot have constructors
334 if (curType.isMixin)
335 err("Mixins cannot have constructors", m.location)
336
337 // ensure super/this constructor is called
338 if (m.ctorChain == null && !compiler.isSys && !curType.base.isObj && !curType.isSynthetic)
339 err("Must call super class constructor in '$m.name'", m.location)
340 }
341
342 //////////////////////////////////////////////////////////////////////////
343 // Statements
344 //////////////////////////////////////////////////////////////////////////
345
346 override Void enterStmt(Stmt stmt)
347 {
348 if (stmt.id == StmtId.tryStmt) protectedRegionDepth++
349 }
350
351 override Void exitStmt(Stmt stmt)
352 {
353 if (stmt.id == StmtId.tryStmt) protectedRegionDepth--
354 }
355
356 override Void enterFinally(TryStmt stmt)
357 {
358 finallyDepth++
359 }
360
361 override Void exitFinally(TryStmt stmt)
362 {
363 finallyDepth--
364 }
365
366 override Void visitStmt(Stmt stmt)
367 {
368 switch (stmt.id)
369 {
370 case StmtId.expr: checkExprStmt((ExprStmt)stmt)
371 case StmtId.ifStmt: checkIf((IfStmt)stmt)
372 case StmtId.returnStmt: checkReturn((ReturnStmt)stmt)
373 case StmtId.throwStmt: checkThrow((ThrowStmt)stmt)
374 case StmtId.forStmt: checkFor((ForStmt)stmt)
375 case StmtId.whileStmt: checkWhile((WhileStmt)stmt)
376 case StmtId.breakStmt: checkBreak((BreakStmt)stmt)
377 case StmtId.continueStmt: checkContinue((ContinueStmt)stmt)
378 case StmtId.tryStmt: checkTry((TryStmt)stmt)
379 case StmtId.switchStmt: checkSwitch((SwitchStmt)stmt)
380 }
381 }
382
383 private Void checkExprStmt(ExprStmt stmt)
384 {
385 if (!stmt.expr.isStmt)
386 err("Not a statement", stmt.expr.location)
387 }
388
389 private Void checkIf(IfStmt stmt)
390 {
391 if (!stmt.condition.ctype.isBool)
392 {
393 if (stmt.condition.ctype.isObj)
394 stmt.condition = cast(stmt.condition, ns.boolType)
395 else
396 err("If condition must be Bool, not '$stmt.condition.ctype'", stmt.condition.location)
397 }
398 }
399
400 private Void checkThrow(ThrowStmt stmt)
401 {
402 if (!stmt.exception.fits(ns.errType))
403 {
404 if (stmt.exception.ctype.isObj)
405 stmt.exception = cast(stmt.exception, ns.errType)
406 else
407 err("Must throw Err, not '$stmt.exception.ctype'", stmt.exception.location)
408 }
409 }
410
411 private Void checkFor(ForStmt stmt)
412 {
413 if (stmt.condition != null && !stmt.condition.ctype.isBool)
414 {
415 if (stmt.condition.ctype.isObj)
416 stmt.condition = cast(stmt.condition, ns.boolType)
417 else
418 err("For condition must be Bool, not '$stmt.condition.ctype'", stmt.condition.location)
419 }
420 }
421
422 private Void checkWhile(WhileStmt stmt)
423 {
424 if (!stmt.condition.ctype.isBool)
425 {
426 if (stmt.condition.ctype.isObj)
427 stmt.condition = cast(stmt.condition, ns.boolType)
428 else
429 err("While condition must be Bool, not '$stmt.condition.ctype'", stmt.condition.location)
430 }
431 }
432
433 private Void checkBreak(BreakStmt stmt)
434 {
435 if (stmt.loop == null)
436 err("Break outside of loop (break is implicit in switch)", stmt.location)
437
438 // can't leave control of a finally block
439 if (finallyDepth > 0)
440 err("Cannot leave finally block", stmt.location)
441 }
442
443 private Void checkContinue(ContinueStmt stmt)
444 {
445 if (stmt.loop == null)
446 err("Continue outside of loop", stmt.location)
447
448 // can't leave control of a finally block
449 if (finallyDepth > 0)
450 err("Cannot leave finally block", stmt.location)
451 }
452
453 private Void checkReturn(ReturnStmt stmt)
454 {
455 ret := curMethod.ret
456 if (stmt.expr == null)
457 {
458 // this is just a sanity check - it should be caught in parser
459 if (!ret.isVoid)
460 err("Must return a value from non-Void method", stmt.location)
461 }
462 else
463 {
464 if (!stmt.expr.fits(ret))
465 {
466 if (stmt.expr.ctype.isObj)
467 stmt.expr = cast(stmt.expr, ret)
468 else
469 err("Cannot return '$stmt.expr.ctype' as '$ret'", stmt.expr.location)
470 }
471 }
472
473 // can't leave control of a finally block
474 if (finallyDepth > 0)
475 err("Cannot leave finally block", stmt.location)
476
477 // add temp local var if returning from a protected region,
478 // we always call this variable "$return" and reuse it if
479 // already declared by a previous return
480 if (stmt.expr != null && protectedRegionDepth > 0)
481 {
482 v := curMethod.vars.find |MethodVar v->Bool| { return v.name == "\$return" }
483 if (v == null) v = curMethod.addLocalVar(stmt.expr.ctype, "\$return", null)
484 stmt.leaveVar = v
485 }
486 }
487
488 private Void checkTry(TryStmt stmt)
489 {
490 caught := CType[,]
491 stmt.catches.each |Catch c|
492 {
493 CType errType := c.errType
494 if (errType == null) errType = ns.errType
495 if (!errType.fits(ns.errType))
496 err("Must catch Err, not '$c.errType'", c.errType.location)
497 else if (errType.fitsAny(caught))
498 err("Already caught '$errType'", c.location)
499 caught.add(errType)
500 }
501 }
502
503 private Void checkSwitch(SwitchStmt stmt)
504 {
505 dups := Int:Int[:]
506
507 stmt.cases.each |Case c|
508 {
509 for (i:=0; i<c.cases.size; ++i)
510 {
511 expr := c.cases[i]
512
513 // check comparability of condition and each case
514 checkCompare(expr, stmt.condition)
515
516 // check for dups
517 literal := expr.asTableSwitchCase
518 if (literal != null)
519 {
520 if (dups[literal] == null)
521 dups[literal] = literal
522 else
523 err("Duplicate case label", expr.location)
524 }
525 }
526 }
527 }
528
529 //////////////////////////////////////////////////////////////////////////
530 // Expr
531 //////////////////////////////////////////////////////////////////////////
532
533 override Expr visitExpr(Expr expr)
534 {
535 switch (expr.id)
536 {
537 case ExprId.rangeLiteral: checkRangeLiteral((RangeLiteralExpr)expr)
538 case ExprId.simpleLiteral: checkSimpleLiteral((SimpleLiteralExpr)expr)
539 case ExprId.boolNot: checkBool((UnaryExpr)expr)
540 case ExprId.assign: checkAssign((BinaryExpr)expr)
541 case ExprId.boolOr:
542 case ExprId.boolAnd: checkBools((CondExpr)expr)
543 case ExprId.same:
544 case ExprId.notSame: checkSame((BinaryExpr)expr)
545 case ExprId.shortcut: checkShortcut((ShortcutExpr)expr)
546 case ExprId.call: checkCall((CallExpr)expr)
547 case ExprId.field: checkField((FieldExpr)expr)
548 case ExprId.thisExpr: checkThis((ThisExpr)expr)
549 case ExprId.superExpr: checkSuper((SuperExpr)expr)
550 case ExprId.isExpr:
551 case ExprId.asExpr:
552 case ExprId.cast: checkTypeCheck((TypeCheckExpr)expr)
553 case ExprId.ternary: checkTernary((TernaryExpr)expr)
554 case ExprId.withBlock: checkWithBlock((WithBlockExpr)expr)
555 }
556 return expr
557 }
558
559 private Void checkRangeLiteral(RangeLiteralExpr range)
560 {
561 if (!range.start.ctype.isInt || !range.end.ctype.isInt)
562 err("Range must be Int..Int, not '${range.start.ctype}..${range.end.ctype}'", range.location)
563 }
564
565 private Void checkSimpleLiteral(SimpleLiteralExpr simple)
566 {
567 t := simple.ctype
568 m := simple.method = t.method("fromStr")
569
570 // resolve fromStr method
571 if (m == null)
572 {
573 err("Simple type '$t' missing 'fromStr' method", simple.location)
574 return
575 }
576
577 // check fromStr is static
578 if (!m.isStatic)
579 err("Simple type '${t}.fromStr' not static method", simple.location)
580
581 // check fromStr return
582 if (m.returnType != t)
583 err("Simple type '${t}.fromStr' returns wrong type", simple.location)
584
585 // check fromStr parameters
586 if (m.params.size < 1)
587 {
588 err("Simple type '${t}.fromStr' not enough parameters", simple.location)
589 }
590 else
591 {
592 if (!m.params[0].paramType.isStr)
593 err("Simple type '${t}.fromStr' first parameter not Str", simple.location)
594 if (m.params.size > 1 && !m.params[1].hasDefault)
595 err("Simple type '${t}.fromStr' too many parameters", simple.location)
596 }
597
598 // check argument is a string
599 argType := simple.arg.ctype
600 if (!argType.isStr)
601 err("Simple literal requires 'Str' argument, not '$argType'", simple.arg.location)
602 }
603
604 private Void checkBool(UnaryExpr expr)
605 {
606 operand := expr.operand.ctype
607 if (!operand.isBool)
608 {
609 if (operand.isObj)
610 expr.operand = cast(expr.operand, ns.boolType)
611 else
612 err("Cannot apply '$expr.opToken.symbol' operator to '$operand'", expr.location)
613 }
614 }
615
616 private Void checkBools(CondExpr expr)
617 {
618 expr.operands.each |Expr operand, Int i|
619 {
620 if (!operand.ctype.isBool)
621 {
622 if (operand.ctype.isObj)
623 expr.operands[i] = cast(operand, ns.boolType)
624 else
625 err("Cannot apply '$expr.opToken.symbol' operator to '$operand.ctype'", operand.location)
626 }
627 }
628 }
629
630 private Void checkSame(BinaryExpr expr)
631 {
632 checkCompare(expr.lhs, expr.rhs)
633 }
634
635 private Void checkCompare(Expr lhs, Expr rhs)
636 {
637 if (!lhs.fits(rhs.ctype) && !rhs.fits(lhs.ctype))
638 err("Incomparable types '$lhs.ctype' and '$rhs.ctype'", lhs.location)
639 }
640
641 private Void checkAssign(BinaryExpr expr)
642 {
643 // check that rhs is assignable to lhs
644 if (!expr.rhs.fits(expr.lhs.ctype))
645 {
646 if (expr.rhs.ctype.isObj)
647 expr.rhs = cast(expr.rhs, expr.lhs.ctype)
648 else
649 err("'$expr.rhs.ctype' is not assignable to '$expr.lhs.ctype'", expr.rhs.location)
650 }
651
652 // check that lhs is assignable
653 if (!expr.lhs.isAssignable)
654 err("Left hand side is not assignable", expr.lhs.location)
655
656 // check left hand side field (common code with checkShortcut)
657 if (expr.lhs.id === ExprId.field)
658 checkAssignField((FieldExpr)expr.lhs, expr.rhs)
659
660 // take this opportunity to generate a temp local variable if needed
661 if (expr.leave && expr.lhs.assignRequiresTempVar)
662 expr.tempVar = curMethod.addLocalVar(expr.ctype, null, null)
663 }
664
665 private Void checkShortcut(ShortcutExpr shortcut)
666 {
667 switch (shortcut.opToken)
668 {
669 // comparable
670 case Token.eq:
671 case Token.notEq:
672 checkCompare(shortcut.target, shortcut.args.first)
673 }
674
675 // if assignment
676 if (shortcut.isAssign)
677 {
678 // check that lhs is assignable
679 if (!shortcut.target.isAssignable)
680 err("Target is not assignable", shortcut.target.location)
681
682 // check left hand side field (common code with checkAssign)
683 if (shortcut.target.id === ExprId.field)
684 checkAssignField((FieldExpr)shortcut.target, shortcut.args.first)
685 }
686
687 // take this oppotunity to generate a temp local variable if needed
688 if (shortcut.leave && shortcut.isAssign && shortcut.target.assignRequiresTempVar)
689 shortcut.tempVar = curMethod.addLocalVar(shortcut.ctype, null, null)
690
691 // perform normal call checking
692 checkCall(shortcut)
693 }
694
695 private Void checkAssignField(FieldExpr lhs, Expr rhs)
696 {
697 field := ((FieldExpr)lhs).field
698
699 // check protection scope (which might be more narrow than the scope
700 // of the entire field as checked in checkProtection by checkField)
701 if (field.setter != null && slotProtectionErr(field) == null)
702 checkSlotProtection(field.setter, lhs.location, true)
703
704 // if not-const we are done
705 if (!field.isConst) return
706
707 // check attempt to set field outside of owning class
708 if (field.parent != curType)
709 {
710 err("Cannot set const field '$field.qname'", lhs.location)
711 return
712 }
713
714 // check attempt to set static field outside of static initializer
715 if (field.isStatic && !curMethod.isStaticInit)
716 {
717 err("Cannot set const static field '$field.name' outside of static initializer", lhs.location)
718 return
719 }
720
721 // check attempt to set instance field outside of ctor
722 if (!field.isStatic && !(curMethod.isInstanceInit || curMethod.isCtor))
723 {
724 err("Cannot set const field '$field.name' outside of constructor", lhs.location)
725 return
726 }
727
728 // if collection, verify that rhs assignment is call to toImmutable()
729 ftype := field.fieldType
730 if (ftype.fits(ns.listType) || ftype.fits(ns.mapType) && rhs != null)
731 {
732 if (rhs.id != ExprId.call || rhs->method->qname != "sys::${ftype.name}.toImmutable")
733 err("Must call ${ftype.name}.toImmutable() when setting const field '$field.name'", rhs.location)
734 }
735 }
736
737 private Void checkCall(CallExpr call)
738 {
739 m := call.method
740 if (m == null)
741 {
742 err("Something wrong with method call?", call.location)
743 return
744 }
745
746 name := m.name
747
748 // check protection scope
749 checkSlotProtection(call.method, call.location)
750
751 // check arguments
752 if (!call.isDynamic) checkArgs(call)
753
754 // if constructor
755 if (m.isCtor && !call.isCtorChain)
756 {
757 // ensure we aren't calling constructors on an instance
758 if (call.target != null && call.target.id !== ExprId.staticTarget)
759 err("Cannot call constructor '$name' on instance", call.location)
760
761 // ensure we aren't calling a constructor on an abstract class
762 if (m.parent.isAbstract)
763 err("Calling constructor on abstract class", call.location)
764 }
765
766 // ensure we aren't calling static methods on an instance
767 if (m.isStatic)
768 {
769 if (call.target != null && call.target.id !== ExprId.staticTarget)
770 err("Cannot call static method '$name' on instance", call.location)
771 }
772
773 // ensure we can't calling an instance method statically
774 if (!m.isStatic && !m.isCtor)
775 {
776 if (call.target == null || call.target.id === ExprId.staticTarget)
777 err("Cannot call instance method '$name' in static context", call.location)
778 }
779
780 // if using super check that concrete
781 if (call.target != null && call.target.id === ExprId.superExpr)
782 {
783 if (m.isAbstract)
784 err("Cannot use super to call abstract method '$m.qname'", call.target.location)
785 }
786 }
787
788 private Void checkField(FieldExpr f)
789 {
790 field := f.field
791
792 // check protection scope
793 checkSlotProtection(field, f.location)
794
795 // ensure we aren't calling static methods on an instance
796 if (field.isStatic)
797 {
798 if (f.target != null && f.target.id !== ExprId.staticTarget)
799 err("Cannot access static field '$f.name' on instance", f.location)
800 }
801
802 // if instance field
803 else
804 {
805 if (f.target == null || f.target.id === ExprId.staticTarget)
806 err("Cannot access instance field '$f.name' in static context", f.location)
807 }
808
809 // if using super check that concrete
810 if (f.target != null && f.target.id === ExprId.superExpr)
811 {
812 if (field.isAbstract)
813 err("Cannot use super to access abstract field '$field.qname'", f.target.location)
814 }
815
816 // if using the field's accessor method
817 if (f.useAccessor)
818 {
819 // check if we can optimize out the accessor (required for constants)
820 f.useAccessor = useFieldAccessor(field)
821
822 // check that we aren't using an field accessor inside of itself
823 if (curMethod != null && (field.getter === curMethod || field.setter === curMethod))
824 err("Cannot use field accessor inside accessor itself - use '@' operator", f.location)
825 }
826
827 // if accessing storage directly
828 else
829 {
830 // check that the current class gets access to direct
831 // field storage (only defining class gets it); allow closures
832 // same scope priviledges as enclosing class
833 enclosing := curType.isClosure ? curType.closure.enclosingType : curType
834 if (!field.isConst && field.parent != curType && field.parent != enclosing)
835 {
836 err("Field storage for '$field.qname' not accessible", f.location)
837 }
838
839 // sanity check that field has storage
840 else if (!field.isStorage)
841 {
842 if (field is FieldDef && ((FieldDef)field).concreteBase != null)
843 err("Field storage of inherited field '${field->concreteBase->qname}' not accessible (might try super)", f.location)
844 else
845 err("Invalid storage access of field '$field.qname' which doesn't have storage", f.location)
846 }
847 }
848 }
849
850 private Bool useFieldAccessor(CField f)
851 {
852 // if there is no getter, then use field directly (constants)
853 if (f.getter == null) return false
854
855 // always use accessor if field is imported from another
856 // pod (in which case it isn't a def in my compilation unit)
857 def := f as FieldDef
858 if (def == null) return true
859
860 // if virtual and/or override then always use accessor
861 if (def.isVirtual || def.isOverride)
862 return true
863
864 // if the field has synthetic getter and setter, then
865 // we can safely optimize internal field accessors to
866 // use field directly
867 if (!def.hasGet && !def.hasSet)
868 return false
869
870 // use accessor since there is a custom getter or setter
871 return true
872 }
873
874 private Void checkThis(ThisExpr expr)
875 {
876 if (inStatic)
877 err("Cannot access 'this' in static context", expr.location)
878 }
879
880 private Void checkSuper(SuperExpr expr)
881 {
882 if (inStatic)
883 err("Cannot access 'super' in static context", expr.location)
884
885 if (curType.isMixin)
886 {
887 if (expr.explicitType == null)
888 err("Must use named 'super' inside mixin", expr.location)
889 else if (!expr.explicitType.isMixin)
890 err("Cannot use 'Obj.super' inside mixin (yeah I know - take it up with Sun)", expr.location)
891 }
892
893 if (expr.explicitType != null)
894 {
895 if (!curType.fits(expr.explicitType))
896 err("Named super '$expr.explicitType' not a super class of '$curType.name'", expr.location)
897 }
898 }
899
900 private Void checkTypeCheck(TypeCheckExpr expr)
901 {
902 check := expr.check
903 target := expr.target.ctype
904 if (!check.fits(target) && !target.fits(check))
905 err("Inconvertible types '$target' and '$check'", expr.location)
906 }
907
908 private Void checkTernary(TernaryExpr expr)
909 {
910 if (!expr.condition.ctype.isBool)
911 {
912 if (expr.condition.ctype.isObj)
913 expr.condition = cast(expr.condition, ns.boolType)
914 else
915 err("Ternary condition must be Bool, not '$expr.condition.ctype'", expr.condition.location)
916 }
917 }
918
919 private Void checkWithBlock(WithBlockExpr expr)
920 {
921 expr.subs.each |Expr sub|
922 {
923 if (!sub.isStmt) err("Not a statement", sub.location)
924 }
925 }
926
927 //////////////////////////////////////////////////////////////////////////
928 // Check Args
929 //////////////////////////////////////////////////////////////////////////
930
931 private Void checkArgs(CallExpr call)
932 {
933 params := call.method.params
934 name := call.name
935 args := call.args
936 isErr := false
937
938 // if we are calling callx(A, B...) on a FuncType, then
939 // use the first class Func signature rather than the
940 // version of callx which got picked because we might have
941 // picked the wrong callx version
942 sig := call.method.parent as FuncType
943 if (sig != null && name.startsWith("call") && name.size == 5)
944 {
945 if (sig.params.size != args.size)
946 {
947 isErr = true
948 }
949 else
950 {
951 sig.params.each |CType p, Int i|
952 {
953 arg := args[i]
954 if (!arg.fits(p))
955 {
956 if (arg.ctype.isObj)
957 args[i] = cast(arg, p)
958 else
959 isErr = true
960 }
961 }
962 }
963 }
964
965 // if more args than params, always an err
966 else if (params.size < args.size)
967 {
968 isErr = true
969 }
970
971 // check each arg against each parameter
972 else
973 {
974 params.each |CParam p, Int i|
975 {
976 if (i >= args.size)
977 {
978 // param has a default value, then that is ok
979 if (!p.hasDefault) isErr = true
980 }
981 else
982 {
983 // ensure arg fits parameter type (or auto-cast)
984 arg := args[i]
985 if (!arg.fits(p.paramType))
986 {
987 if (arg.ctype.isObj)
988 args[i] = cast(arg, p.paramType)
989 else
990 isErr = true
991 }
992 }
993 }
994 }
995
996 if (!isErr) return
997
998 msg := "Invalid args "
999 if (sig != null)
1000 msg += "|" + sig.params.join(", ") + "|"
1001 else
1002 msg += call.method.nameAndParamTypesToStr
1003 msg += ", not (" + args.join(", ", |Expr e->Str| { return "$e.ctype" }) + ")"
1004 err(msg, call.location)
1005 }
1006
1007 //////////////////////////////////////////////////////////////////////////
1008 // Flag Utils
1009 //////////////////////////////////////////////////////////////////////////
1010
1011 private Void checkProtectionFlags(Int flags, Location loc)
1012 {
1013 isPublic := flags & FConst.Public != 0
1014 isProtected := flags & FConst.Protected != 0
1015 isPrivate := flags & FConst.Private != 0
1016 isInternal := flags & FConst.Internal != 0
1017 isVirtual := flags & FConst.Virtual != 0
1018 isOverride := flags & FConst.Override != 0
1019
1020 if (isPublic)
1021 {
1022 if (isProtected) err("Invalid combination of 'public' and 'protected' modifiers", loc)
1023 if (isPrivate) err("Invalid combination of 'public' and 'private' modifiers", loc)
1024 if (isInternal) err("Invalid combination of 'public' and 'internal' modifiers", loc)
1025 }
1026 else if (isProtected)
1027 {
1028 if (isPrivate) err("Invalid combination of 'protected' and 'private' modifiers", loc)
1029 if (isInternal) err("Invalid combination of 'protected' and 'internal' modifiers", loc)
1030 }
1031 else if (isPrivate)
1032 {
1033 if (isInternal) err("Invalid combination of 'private' and 'internal' modifiers", loc)
1034 if (isVirtual && !isOverride) err("Invalid combination of 'private' and 'virtual' modifiers", loc)
1035 }
1036 }
1037
1038 private Void checkSlotProtection(CSlot slot, Location loc, Bool setter := false)
1039 {
1040 errMsg := slotProtectionErr(slot, setter)
1041 if (errMsg != null) err(errMsg, loc)
1042 }
1043
1044 private Str slotProtectionErr(CSlot slot, Bool setter := false)
1045 {
1046 msg := setter ? "setter of field" : (slot is CMethod ? "method" : "field")
1047
1048 // short circuit if method on myself
1049 if (curType == slot.parent)
1050 return null
1051
1052 // allow closures same scope priviledges as enclosing class
1053 myType := curType
1054 if (myType.isClosure)
1055 myType = curType.closure.enclosingType
1056
1057 if (slot.isPrivate && myType != slot.parent)
1058 return "Private $msg '$slot.qname' not accessible"
1059
1060 else if (slot.isProtected && !myType.fits(slot.parent))
1061 return "Protected $msg '$slot.qname' not accessible"
1062
1063 else if (slot.isInternal && myType.pod != slot.parent.pod)
1064 return "Internal $msg '$slot.qname' not accessible"
1065
1066 else
1067 return null
1068 }
1069
1070 //////////////////////////////////////////////////////////////////////////
1071 // Utils
1072 //////////////////////////////////////////////////////////////////////////
1073
1074 private static Expr cast(Expr target, CType to)
1075 {
1076 return TypeCheckExpr.cast(target, to)
1077 }
1078
1079 //////////////////////////////////////////////////////////////////////////
1080 // Fields
1081 //////////////////////////////////////////////////////////////////////////
1082
1083 private Int protectedRegionDepth := 0 // try statement depth
1084 private Int finallyDepth := 0 // finally block depth
1085 }
More Info
Slots
- cast
- checkAbstractSlots
- checkArgs
- checkAssign
- checkAssignField
- checkBool
- checkBools
- checkBreak
- checkCall
- checkCompare
- checkConstType
- checkContinue
- checkCtor
- checkExprStmt
- checkField
- checkFieldFlags
- checkFor
- checkIf
- checkMethodFlags
- checkParams
- checkProtectionFlags
- checkRangeLiteral
- checkReturn
- checkSame
- checkShortcut
- checkSimpleLiteral
- checkSlotProtection
- checkSuper
- checkSwitch
- checkTernary
- checkThis
- checkThrow
- checkTry
- checkTypeCheck
- checkTypeFlags
- checkWhile
- checkWithBlock
- enterFinally
- enterStmt
- exitFinally
- exitStmt
- finallyDepth
- isConstFieldType
- make
- protectedRegionDepth
- run
- slotProtectionErr
- useFieldAccessor
- visitExpr
- visitFieldDef
- visitMethodDef
- visitStmt
- visitTypeDef