diff options
Diffstat (limited to 'compiler/sempass2.nim')
-rw-r--r-- | compiler/sempass2.nim | 91 |
1 files changed, 73 insertions, 18 deletions
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index bdc4e7028..89d8d45d8 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -8,7 +8,8 @@ # import - ast, astalgo, msgs, renderer, magicsys, types, idents, trees, wordrecg + intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, + wordrecg # Second semantic checking pass over the AST. Necessary because the old # way had some inherent problems. Performs: @@ -48,8 +49,6 @@ when false: proc sem2call(c: PContext, n: PNode): PNode = assert n.kind in nkCallKinds - - proc sem2sym(c: PContext, n: PNode): PNode = assert n.kind == nkSym @@ -97,16 +96,17 @@ proc excType(n: PNode): PType = else: n.sons[0].typ result = skipTypes(t, skipPtrs) -proc addEffect(a: PEffects, e: PNode) = +proc addEffect(a: PEffects, e: PNode, useLineInfo=true) = assert e.kind == nkRaiseStmt var aa = a.exc for i in a.bottom .. <aa.len: - if sameType(aa[i].excType, e.excType) and aa[i].info == e.info: return + if sameType(aa[i].excType, e.excType): + if not useLineInfo: return + elif aa[i].info == e.info: return throws(a, e) -proc mergeEffects(a: PEffects, b: PNode) = - for effect in items(b): - addEffect(a, effect) +proc mergeEffects(a: PEffects, b: PNode, useLineInfo) = + for effect in items(b): addEffect(a, effect, useLineInfo) proc listEffects(a: PEffects) = var aa = a.exc @@ -118,12 +118,13 @@ proc catches(tracked: PEffects, e: PType) = var L = tracked.exc.len var i = tracked.bottom while i < L: - # e supertype of r? - if inheritanceDiff(e, tracked.exc[i].excType) <= 0: + # r supertype of e? + if inheritanceDiff(tracked.exc[i].excType, e) <= 0: tracked.exc.sons[i] = tracked.exc.sons[L-1] dec L else: inc i + setLen(tracked.exc.sons, L) proc catchesAll(tracked: PEffects) = setLen(tracked.exc.sons, tracked.bottom) @@ -161,6 +162,20 @@ proc trackPragmaStmt(tracked: PEffects, n: PNode) = # list the computed effects up to here: listEffects(tracked) +proc raisesSpec(n: PNode): PNode = + for i in countup(0, sonsLen(n) - 1): + var it = n.sons[i] + if it.kind == nkExprColonExpr and whichPragma(it) == wRaises: + result = it.sons[1] + if result.kind notin {nkCurly, nkBracket}: + result = newNodeI(nkCurly, result.info) + result.add(it.sons[1]) + return + +proc createRaise(n: PNode, t: PType): PNode = + result = newNodeI(nkRaiseStmt, n.info) + result.add(newNodeIT(nkType, n.info, t)) + proc track(tracked: PEffects, n: PNode) = case n.kind of nkRaiseStmt: throws(tracked, n) @@ -168,18 +183,20 @@ proc track(tracked: PEffects, n: PNode) = # p's effects are ours too: let op = n.sons[0].typ if op != nil and op.kind == tyProc: - InternalAssert op.kind == tyProc and op.n.sons[0].kind == nkEffectList + InternalAssert op.n.sons[0].kind == nkEffectList var effectList = op.n.sons[0] if effectList.len == 0: - if isIndirectCall(n.sons[0]) or isForwardedProc(n.sons[0]): - # assume the worst: raise of exception 'E_Base': - var rs = newNodeI(nkRaiseStmt, n.info) - var re = newNodeIT(nkType, n.info, sysTypeFromName"E_Base") - rs.add(re) - addEffect(tracked, rs) + if isForwardedProc(n.sons[0]): + let spec = raisesSpec(n.sons[0].sym.ast.sons[pragmasPos]) + if not isNil(spec): + mergeEffects(tracked, spec, useLineInfo=false) + else: + addEffect(tracked, createRaise(n, sysTypeFromName"E_Base")) + elif isIndirectCall(n.sons[0]): + addEffect(tracked, createRaise(n, sysTypeFromName"E_Base")) else: effectList = effectList.sons[exceptionEffects] - mergeEffects(tracked, effectList) + mergeEffects(tracked, effectList, useLineInfo=true) of nkTryStmt: trackTryStmt(tracked, n) return @@ -191,10 +208,45 @@ proc track(tracked: PEffects, n: PNode) = for i in 0 .. <safeLen(n): track(tracked, n.sons[i]) +# XXX +# - make use of 'raises' in proc types compatibility +# - check for 'raises' consistency for multi-methods + +proc checkRaisesSpec(spec, real: PNode) = + # check that any real exception is listed in 'spec'; mark those as used; + # report any unused exception + var used = initIntSet() + for r in items(real): + block search: + for s in 0 .. <spec.len: + # r supertype of s? + if inheritanceDiff(r.excType, spec[s].typ) <= 0: + used.incl(s) + break search + # XXX call graph analysis would be nice here! + localError(r.info, errGenerated, "can raise an unlisted exception: " & + typeToString(r.sons[0].typ)) + # hint about unnecessarily listed exception types: + for s in 0 .. <spec.len: + if not used.contains(s): + Message(spec[s].info, hintXDeclaredButNotUsed, renderTree(spec[s])) + +proc compatibleEffects*(formal, actual: PType): bool = + # for proc type compatibility checking: + assert formal.kind == tyProc and actual.kind == tyProc + InternalAssert formal.n.sons[0].kind == nkEffectList + InternalAssert actual.n.sons[0].kind == nkEffectList + + var effectList = formal.n.sons[0] + if effectList.len == 0: + # 'formal' has no restrictions :-) + result = true + proc trackProc*(s: PSym, body: PNode) = var effects = s.typ.n.sons[0] InternalAssert effects.kind == nkEffectList # effects already computed? + if sfForward in s.flags: return if effects.len == effectListLen: return newSeq(effects.sons, effectListLen) effects.sons[exceptionEffects] = newNodeI(nkArgList, body.info) @@ -203,3 +255,6 @@ proc trackProc*(s: PSym, body: PNode) = t.exc = effects.sons[exceptionEffects] track(t, body) + let spec = raisesSpec(s.ast.sons[pragmasPos]) + if not isNil(spec): + checkRaisesSpec(spec, t.exc) |