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.debug("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 checkParam(p)
320 if (seenDef)
321 {
322 if (p.def == null)
323 err("Parameter '$p.name' must have default", p.location)
324 }
325 else
326 {
327 seenDef = p.def != null
328 }
329 }
330 }
331
332 private Void checkParam(ParamDef p)
333 {
334 // check parameter default type
335 if (p.def != null && !p.def.fits(p.paramType))
336 err("'$p.def.ctype' is not assignable to '$p.paramType'", p.def.location)
337 }
338
339 private Void checkCtor(MethodDef m)
340 {
341 // mixins cannot have constructors
342 if (curType.isMixin)
343 err("Mixins cannot have constructors", m.location)
344
345 // ensure super/this constructor is called
346 if (m.ctorChain == null && !compiler.isSys && !curType.base.isObj && !curType.isSynthetic)
347 err("Must call super class constructor in '$m.name'", m.location)
348 }
349
350 //////////////////////////////////////////////////////////////////////////
351 // Statements
352 //////////////////////////////////////////////////////////////////////////
353
354 override Void enterStmt(Stmt stmt)
355 {
356 if (stmt.id == StmtId.tryStmt) protectedRegionDepth++
357 }
358
359 override Void exitStmt(Stmt stmt)
360 {
361 if (stmt.id == StmtId.tryStmt) protectedRegionDepth--
362 }
363
364 override Void enterFinally(TryStmt stmt)
365 {
366 finallyDepth++
367 }
368
369 override Void exitFinally(TryStmt stmt)
370 {
371 finallyDepth--
372 }
373
374 override Void visitStmt(Stmt stmt)
375 {
376 switch (stmt.id)
377 {
378 case StmtId.expr: checkExprStmt((ExprStmt)stmt)
379 case StmtId.ifStmt: checkIf((IfStmt)stmt)
380 case StmtId.returnStmt: checkReturn((ReturnStmt)stmt)
381 case StmtId.throwStmt: checkThrow((ThrowStmt)stmt)
382 case StmtId.forStmt: checkFor((ForStmt)stmt)
383 case StmtId.whileStmt: checkWhile((WhileStmt)stmt)
384 case StmtId.breakStmt: checkBreak((BreakStmt)stmt)
385 case StmtId.continueStmt: checkContinue((ContinueStmt)stmt)
386 case StmtId.tryStmt: checkTry((TryStmt)stmt)
387 case StmtId.switchStmt: checkSwitch((SwitchStmt)stmt)
388 }
389 }
390
391 private Void checkExprStmt(ExprStmt stmt)
392 {
393 if (!stmt.expr.isStmt)
394 err("Not a statement", stmt.expr.location)
395 }
396
397 private Void checkIf(IfStmt stmt)
398 {
399 if (!stmt.condition.ctype.isBool)
400 {
401 if (stmt.condition.ctype.isObj)
402 stmt.condition = cast(stmt.condition, ns.boolType)
403 else
404 err("If condition must be Bool, not '$stmt.condition.ctype'", stmt.condition.location)
405 }
406 }
407
408 private Void checkThrow(ThrowStmt stmt)
409 {
410 if (!stmt.exception.fits(ns.errType))
411 {
412 if (stmt.exception.ctype.isObj)
413 stmt.exception = cast(stmt.exception, ns.errType)
414 else
415 err("Must throw Err, not '$stmt.exception.ctype'", stmt.exception.location)
416 }
417 }
418
419 private Void checkFor(ForStmt stmt)
420 {
421 if (stmt.condition != null && !stmt.condition.ctype.isBool)
422 {
423 if (stmt.condition.ctype.isObj)
424 stmt.condition = cast(stmt.condition, ns.boolType)
425 else
426 err("For condition must be Bool, not '$stmt.condition.ctype'", stmt.condition.location)
427 }
428 }
429
430 private Void checkWhile(WhileStmt stmt)
431 {
432 if (!stmt.condition.ctype.isBool)
433 {
434 if (stmt.condition.ctype.isObj)
435 stmt.condition = cast(stmt.condition, ns.boolType)
436 else
437 err("While condition must be Bool, not '$stmt.condition.ctype'", stmt.condition.location)
438 }
439 }
440
441 private Void checkBreak(BreakStmt stmt)
442 {
443 if (stmt.loop == null)
444 err("Break outside of loop (break is implicit in switch)", stmt.location)
445
446 // can't leave control of a finally block
447 if (finallyDepth > 0)
448 err("Cannot leave finally block", stmt.location)
449 }
450
451 private Void checkContinue(ContinueStmt stmt)
452 {
453 if (stmt.loop == null)
454 err("Continue outside of loop", stmt.location)
455
456 // can't leave control of a finally block
457 if (finallyDepth > 0)
458 err("Cannot leave finally block", stmt.location)
459 }
460
461 private Void checkReturn(ReturnStmt stmt)
462 {
463 ret := curMethod.ret
464 if (stmt.expr == null)
465 {
466 // this is just a sanity check - it should be caught in parser
467 if (!ret.isVoid)
468 err("Must return a value from non-Void method", stmt.location)
469 }
470 else
471 {
472 if (!stmt.expr.fits(ret))
473 {
474 if (stmt.expr.ctype.isObj)
475 stmt.expr = cast(stmt.expr, ret)
476 else
477 err("Cannot return '$stmt.expr.ctype' as '$ret'", stmt.expr.location)
478 }
479 }
480
481 // can't leave control of a finally block
482 if (finallyDepth > 0)
483 err("Cannot leave finally block", stmt.location)
484
485 // add temp local var if returning from a protected region,
486 // we always call this variable "$return" and reuse it if
487 // already declared by a previous return
488 if (stmt.expr != null && protectedRegionDepth > 0)
489 {
490 v := curMethod.vars.find |MethodVar v->Bool| { return v.name == "\$return" }
491 if (v == null) v = curMethod.addLocalVar(stmt.expr.ctype, "\$return", null)
492 stmt.leaveVar = v
493 }
494 }
495
496 private Void checkTry(TryStmt stmt)
497 {
498 caught := CType[,]
499 stmt.catches.each |Catch c|
500 {
501 CType errType := c.errType
502 if (errType == null) errType = ns.errType
503 if (!errType.fits(ns.errType))
504 err("Must catch Err, not '$c.errType'", c.errType.location)
505 else if (errType.fitsAny(caught))
506 err("Already caught '$errType'", c.location)
507 caught.add(errType)
508 }
509 }
510
511 private Void checkSwitch(SwitchStmt stmt)
512 {
513 dups := Int:Int[:]
514
515 stmt.cases.each |Case c|
516 {
517 for (i:=0; i<c.cases.size; ++i)
518 {
519 expr := c.cases[i]
520
521 // check comparability of condition and each case
522 checkCompare(expr, stmt.condition)
523
524 // check for dups
525 literal := expr.asTableSwitchCase
526 if (literal != null)
527 {
528 if (dups[literal] == null)
529 dups[literal] = literal
530 else
531 err("Duplicate case label", expr.location)
532 }
533 }
534 }
535 }
536
537 //////////////////////////////////////////////////////////////////////////
538 // Expr
539 //////////////////////////////////////////////////////////////////////////
540
541 override Expr visitExpr(Expr expr)
542 {
543 switch (expr.id)
544 {
545 case ExprId.rangeLiteral: checkRangeLiteral((RangeLiteralExpr)expr)
546 case ExprId.simpleLiteral: checkSimpleLiteral((SimpleLiteralExpr)expr)
547 case ExprId.boolNot: checkBool((UnaryExpr)expr)
548 case ExprId.assign: checkAssign((BinaryExpr)expr)
549 case ExprId.boolOr:
550 case ExprId.boolAnd: checkBools((CondExpr)expr)
551 case ExprId.same:
552 case ExprId.notSame: checkSame((BinaryExpr)expr)
553 case ExprId.shortcut: checkShortcut((ShortcutExpr)expr)
554 case ExprId.call: checkCall((CallExpr)expr)
555 case ExprId.field: checkField((FieldExpr)expr)
556 case ExprId.thisExpr: checkThis((ThisExpr)expr)
557 case ExprId.superExpr: checkSuper((SuperExpr)expr)
558 case ExprId.isExpr:
559 case ExprId.asExpr:
560 case ExprId.cast: checkTypeCheck((TypeCheckExpr)expr)
561 case ExprId.ternary: checkTernary((TernaryExpr)expr)
562 case ExprId.withBlock: checkWithBlock((WithBlockExpr)expr)
563 }
564 return expr
565 }
566
567 private Void checkRangeLiteral(RangeLiteralExpr range)
568 {
569 if (!range.start.ctype.isInt || !range.end.ctype.isInt)
570 err("Range must be Int..Int, not '${range.start.ctype}..${range.end.ctype}'", range.location)
571 }
572
573 private Void checkSimpleLiteral(SimpleLiteralExpr simple)
574 {
575 t := simple.ctype
576 m := simple.method = t.method("fromStr")
577
578 // resolve fromStr method
579 if (m == null)
580 {
581 err("Simple type '$t' missing 'fromStr' method", simple.location)
582 return
583 }
584
585 // check fromStr is static
586 if (!m.isStatic)
587 err("Simple type '${t}.fromStr' not static method", simple.location)
588
589 // check fromStr return
590 if (m.returnType != t)
591 err("Simple type '${t}.fromStr' returns wrong type", simple.location)
592
593 // check fromStr parameters
594 if (m.params.size < 1)
595 {
596 err("Simple type '${t}.fromStr' not enough parameters", simple.location)
597 }
598 else
599 {
600 if (!m.params[0].paramType.isStr)
601 err("Simple type '${t}.fromStr' first parameter not Str", simple.location)
602 if (m.params.size > 1 && !m.params[1].hasDefault)
603 err("Simple type '${t}.fromStr' too many parameters", simple.location)
604 }
605
606 // check argument is a string
607 argType := simple.arg.ctype
608 if (!argType.isStr)
609 err("Simple literal requires 'Str' argument, not '$argType'", simple.arg.location)
610 }
611
612 private Void checkBool(UnaryExpr expr)
613 {
614 operand := expr.operand.ctype
615 if (!operand.isBool)
616 {
617 if (operand.isObj)
618 expr.operand = cast(expr.operand, ns.boolType)
619 else
620 err("Cannot apply '$expr.opToken.symbol' operator to '$operand'", expr.location)
621 }
622 }
623
624 private Void checkBools(CondExpr expr)
625 {
626 expr.operands.each |Expr operand, Int i|
627 {
628 if (!operand.ctype.isBool)
629 {
630 if (operand.ctype.isObj)
631 expr.operands[i] = cast(operand, ns.boolType)
632 else
633 err("Cannot apply '$expr.opToken.symbol' operator to '$operand.ctype'", operand.location)
634 }
635 }
636 }
637
638 private Void checkSame(BinaryExpr expr)
639 {
640 checkCompare(expr.lhs, expr.rhs)
641 }
642
643 private Void checkCompare(Expr lhs, Expr rhs)
644 {
645 if (!lhs.fits(rhs.ctype) && !rhs.fits(lhs.ctype))
646 err("Incomparable types '$lhs.ctype' and '$rhs.ctype'", lhs.location)
647 }
648
649 private Void checkAssign(BinaryExpr expr)
650 {
651 // check that rhs is assignable to lhs
652 if (!expr.rhs.fits(expr.lhs.ctype))
653 {
654 if (expr.rhs.ctype.isObj)
655 expr.rhs = cast(expr.rhs, expr.lhs.ctype)
656 else
657 err("'$expr.rhs.ctype' is not assignable to '$expr.lhs.ctype'", expr.rhs.location)
658 }
659
660 // check that lhs is assignable
661 if (!expr.lhs.isAssignable)
662 err("Left hand side is not assignable", expr.lhs.location)
663
664 // check left hand side field (common code with checkShortcut)
665 if (expr.lhs.id === ExprId.field)
666 checkAssignField((FieldExpr)expr.lhs, expr.rhs)
667
668 // take this opportunity to generate a temp local variable if needed
669 if (expr.leave && expr.lhs.assignRequiresTempVar)
670 expr.tempVar = curMethod.addLocalVar(expr.ctype, null, null)
671 }
672
673 private Void checkShortcut(ShortcutExpr shortcut)
674 {
675 switch (shortcut.opToken)
676 {
677 // comparable
678 case Token.eq:
679 case Token.notEq:
680 checkCompare(shortcut.target, shortcut.args.first)
681 }
682
683 // if assignment
684 if (shortcut.isAssign)
685 {
686 // check that lhs is assignable
687 if (!shortcut.target.isAssignable)
688 err("Target is not assignable", shortcut.target.location)
689
690 // check left hand side field (common code with checkAssign)
691 if (shortcut.target.id === ExprId.field)
692 checkAssignField((FieldExpr)shortcut.target, shortcut.args.first)
693 }
694
695 // take this oppotunity to generate a temp local variable if needed
696 if (shortcut.leave && shortcut.isAssign && shortcut.target.assignRequiresTempVar)
697 shortcut.tempVar = curMethod.addLocalVar(shortcut.ctype, null, null)
698
699 // perform normal call checking
700 checkCall(shortcut)
701 }
702
703 private Void checkAssignField(FieldExpr lhs, Expr rhs)
704 {
705 field := ((FieldExpr)lhs).field
706
707 // check protection scope (which might be more narrow than the scope
708 // of the entire field as checked in checkProtection by checkField)
709 if (field.setter != null && slotProtectionErr(field) == null)
710 checkSlotProtection(field.setter, lhs.location, true)
711
712 // if not-const we are done
713 if (!field.isConst) return
714
715 // check attempt to set field outside of owning class
716 if (field.parent != curType)
717 {
718 err("Cannot set const field '$field.qname'", lhs.location)
719 return
720 }
721
722 // check attempt to set static field outside of static initializer
723 if (field.isStatic && !curMethod.isStaticInit)
724 {
725 err("Cannot set const static field '$field.name' outside of static initializer", lhs.location)
726 return
727 }
728
729 // check attempt to set instance field outside of ctor
730 if (!field.isStatic && !(curMethod.isInstanceInit || curMethod.isCtor))
731 {
732 err("Cannot set const field '$field.name' outside of constructor", lhs.location)
733 return
734 }
735
736 // if collection, verify that rhs assignment is call to toImmutable()
737 ftype := field.fieldType
738 if (ftype.fits(ns.listType) || ftype.fits(ns.mapType) && rhs != null)
739 {
740 if (rhs.id != ExprId.call || rhs->method->qname != "sys::${ftype.name}.toImmutable")
741 err("Must call ${ftype.name}.toImmutable() when setting const field '$field.name'", rhs.location)
742 }
743 }
744
745 private Void checkCall(CallExpr call)
746 {
747 m := call.method
748 if (m == null)
749 {
750 err("Something wrong with method call?", call.location)
751 return
752 }
753
754 name := m.name
755
756 // check protection scope
757 checkSlotProtection(call.method, call.location)
758
759 // check arguments
760 if (!call.isDynamic) checkArgs(call)
761
762 // if constructor
763 if (m.isCtor && !call.isCtorChain)
764 {
765 // ensure we aren't calling constructors on an instance
766 if (call.target != null && call.target.id !== ExprId.staticTarget)
767 err("Cannot call constructor '$name' on instance", call.location)
768
769 // ensure we aren't calling a constructor on an abstract class
770 if (m.parent.isAbstract)
771 err("Calling constructor on abstract class", call.location)
772 }
773
774 // ensure we aren't calling static methods on an instance
775 if (m.isStatic)
776 {
777 if (call.target != null && call.target.id !== ExprId.staticTarget)
778 err("Cannot call static method '$name' on instance", call.location)
779 }
780
781 // ensure we can't calling an instance method statically
782 if (!m.isStatic && !m.isCtor)
783 {
784 if (call.target == null || call.target.id === ExprId.staticTarget)
785 err("Cannot call instance method '$name' in static context", call.location)
786 }
787
788 // if using super check that concrete
789 if (call.target != null && call.target.id === ExprId.superExpr)
790 {
791 if (m.isAbstract)
792 err("Cannot use super to call abstract method '$m.qname'", call.target.location)
793 }
794 }
795
796 private Void checkField(FieldExpr f)
797 {
798 field := f.field
799
800 // check protection scope
801 checkSlotProtection(field, f.location)
802
803 // ensure we aren't calling static methods on an instance
804 if (field.isStatic)
805 {
806 if (f.target != null && f.target.id !== ExprId.staticTarget)
807 err("Cannot access static field '$f.name' on instance", f.location)
808 }
809
810 // if instance field
811 else
812 {
813 if (f.target == null || f.target.id === ExprId.staticTarget)
814 err("Cannot access instance field '$f.name' in static context", f.location)
815 }
816
817 // if using super check that concrete
818 if (f.target != null && f.target.id === ExprId.superExpr)
819 {
820 if (field.isAbstract)
821 err("Cannot use super to access abstract field '$field.qname'", f.target.location)
822 }
823
824 // if using the field's accessor method
825 if (f.useAccessor)
826 {
827 // check if we can optimize out the accessor (required for constants)
828 f.useAccessor = useFieldAccessor(field)
829
830 // check that we aren't using an field accessor inside of itself
831 if (curMethod != null && (field.getter === curMethod || field.setter === curMethod))
832 err("Cannot use field accessor inside accessor itself - use '@' operator", f.location)
833 }
834
835 // if accessing storage directly
836 else
837 {
838 // check that the current class gets access to direct
839 // field storage (only defining class gets it); allow closures
840 // same scope priviledges as enclosing class
841 enclosing := curType.isClosure ? curType.closure.enclosingType : curType
842 if (!field.isConst && field.parent != curType && field.parent != enclosing)
843 {
844 err("Field storage for '$field.qname' not accessible", f.location)
845 }
846
847 // sanity check that field has storage
848 else if (!field.isStorage)
849 {
850 if (field is FieldDef && ((FieldDef)field).concreteBase != null)
851 err("Field storage of inherited field '${field->concreteBase->qname}' not accessible (might try super)", f.location)
852 else
853 err("Invalid storage access of field '$field.qname' which doesn't have storage", f.location)
854 }
855 }
856 }
857
858 private Bool useFieldAccessor(CField f)
859 {
860 // if there is no getter, then use field directly (constants)
861 if (f.getter == null) return false
862
863 // always use accessor if field is imported from another
864 // pod (in which case it isn't a def in my compilation unit)
865 def := f as FieldDef
866 if (def == null) return true
867
868 // if virtual and/or override then always use accessor
869 if (def.isVirtual || def.isOverride)
870 return true
871
872 // if the field has synthetic getter and setter, then
873 // we can safely optimize internal field accessors to
874 // use field directly
875 if (!def.hasGet && !def.hasSet)
876 return false
877
878 // use accessor since there is a custom getter or setter
879 return true
880 }
881
882 private Void checkThis(ThisExpr expr)
883 {
884 if (inStatic)
885 err("Cannot access 'this' in static context", expr.location)
886 }
887
888 private Void checkSuper(SuperExpr expr)
889 {
890 if (inStatic)
891 err("Cannot access 'super' in static context", expr.location)
892
893 if (curType.isMixin)
894 {
895 if (expr.explicitType == null)
896 err("Must use named 'super' inside mixin", expr.location)
897 else if (!expr.explicitType.isMixin)
898 err("Cannot use 'Obj.super' inside mixin (yeah I know - take it up with Sun)", expr.location)
899 }
900
901 if (expr.explicitType != null)
902 {
903 if (!curType.fits(expr.explicitType))
904 err("Named super '$expr.explicitType' not a super class of '$curType.name'", expr.location)
905 }
906 }
907
908 private Void checkTypeCheck(TypeCheckExpr expr)
909 {
910 check := expr.check
911 target := expr.target.ctype
912 if (!check.fits(target) && !target.fits(check))
913 err("Inconvertible types '$target' and '$check'", expr.location)
914 }
915
916 private Void checkTernary(TernaryExpr expr)
917 {
918 if (!expr.condition.ctype.isBool)
919 {
920 if (expr.condition.ctype.isObj)
921 expr.condition = cast(expr.condition, ns.boolType)
922 else
923 err("Ternary condition must be Bool, not '$expr.condition.ctype'", expr.condition.location)
924 }
925 }
926
927 private Void checkWithBlock(WithBlockExpr expr)
928 {
929 expr.subs.each |Expr sub|
930 {
931 if (!sub.isStmt) err("Not a statement", sub.location)
932 }
933 }
934
935 //////////////////////////////////////////////////////////////////////////
936 // Check Args
937 //////////////////////////////////////////////////////////////////////////
938
939 private Void checkArgs(CallExpr call)
940 {
941 params := call.method.params
942 name := call.name
943 args := call.args
944 isErr := false
945
946 // if we are calling callx(A, B...) on a FuncType, then
947 // use the first class Func signature rather than the
948 // version of callx which got picked because we might have
949 // picked the wrong callx version
950 sig := call.method.parent as FuncType
951 if (sig != null && name.startsWith("call") && name.size == 5)
952 {
953 if (sig.params.size != args.size)
954 {
955 isErr = true
956 }
957 else
958 {
959 sig.params.each |CType p, Int i|
960 {
961 arg := args[i]
962 if (!arg.fits(p))
963 {
964 if (arg.ctype.isObj)
965 args[i] = cast(arg, p)
966 else
967 isErr = true
968 }
969 }
970 }
971 }
972
973 // if more args than params, always an err
974 else if (params.size < args.size)
975 {
976 isErr = true
977 }
978
979 // check each arg against each parameter
980 else
981 {
982 params.each |CParam p, Int i|
983 {
984 if (i >= args.size)
985 {
986 // param has a default value, then that is ok
987 if (!p.hasDefault) isErr = true
988 }
989 else
990 {
991 // ensure arg fits parameter type (or auto-cast)
992 arg := args[i]
993 if (!arg.fits(p.paramType))
994 {
995 if (arg.ctype.isObj)
996 args[i] = cast(arg, p.paramType)
997 else
998 isErr = true
999 }
1000 }
1001 }
1002 }
1003
1004 if (!isErr) return
1005
1006 msg := "Invalid args "
1007 if (sig != null)
1008 msg += "|" + sig.params.join(", ") + "|"
1009 else
1010 msg += call.method.nameAndParamTypesToStr
1011 msg += ", not (" + args.join(", ", |Expr e->Str| { return "$e.ctype" }) + ")"
1012 err(msg, call.location)
1013 }
1014
1015 //////////////////////////////////////////////////////////////////////////
1016 // Flag Utils
1017 //////////////////////////////////////////////////////////////////////////
1018
1019 private Void checkProtectionFlags(Int flags, Location loc)
1020 {
1021 isPublic := flags & FConst.Public != 0
1022 isProtected := flags & FConst.Protected != 0
1023 isPrivate := flags & FConst.Private != 0
1024 isInternal := flags & FConst.Internal != 0
1025 isVirtual := flags & FConst.Virtual != 0
1026 isOverride := flags & FConst.Override != 0
1027
1028 if (isPublic)
1029 {
1030 if (isProtected) err("Invalid combination of 'public' and 'protected' modifiers", loc)
1031 if (isPrivate) err("Invalid combination of 'public' and 'private' modifiers", loc)
1032 if (isInternal) err("Invalid combination of 'public' and 'internal' modifiers", loc)
1033 }
1034 else if (isProtected)
1035 {
1036 if (isPrivate) err("Invalid combination of 'protected' and 'private' modifiers", loc)
1037 if (isInternal) err("Invalid combination of 'protected' and 'internal' modifiers", loc)
1038 }
1039 else if (isPrivate)
1040 {
1041 if (isInternal) err("Invalid combination of 'private' and 'internal' modifiers", loc)
1042 if (isVirtual && !isOverride) err("Invalid combination of 'private' and 'virtual' modifiers", loc)
1043 }
1044 }
1045
1046 private Void checkSlotProtection(CSlot slot, Location loc, Bool setter := false)
1047 {
1048 errMsg := slotProtectionErr(slot, setter)
1049 if (errMsg != null) err(errMsg, loc)
1050 }
1051
1052 private Str slotProtectionErr(CSlot slot, Bool setter := false)
1053 {
1054 msg := setter ? "setter of field" : (slot is CMethod ? "method" : "field")
1055
1056 // short circuit if method on myself
1057 if (curType == slot.parent)
1058 return null
1059
1060 // allow closures same scope priviledges as enclosing class
1061 myType := curType
1062 if (myType.isClosure)
1063 myType = curType.closure.enclosingType
1064
1065 if (slot.isPrivate && myType != slot.parent)
1066 return "Private $msg '$slot.qname' not accessible"
1067
1068 else if (slot.isProtected && !myType.fits(slot.parent))
1069 return "Protected $msg '$slot.qname' not accessible"
1070
1071 else if (slot.isInternal && myType.pod != slot.parent.pod)
1072 return "Internal $msg '$slot.qname' not accessible"
1073
1074 else
1075 return null
1076 }
1077
1078 //////////////////////////////////////////////////////////////////////////
1079 // Utils
1080 //////////////////////////////////////////////////////////////////////////
1081
1082 private static Expr cast(Expr target, CType to)
1083 {
1084 return TypeCheckExpr.cast(target, to)
1085 }
1086
1087 //////////////////////////////////////////////////////////////////////////
1088 // Fields
1089 //////////////////////////////////////////////////////////////////////////
1090
1091 private Int protectedRegionDepth := 0 // try statement depth
1092 private Int finallyDepth := 0 // finally block depth
1093 }