completed expr/stmt unification

This commit is contained in:
Araq 2013-05-03 23:51:43 +02:00
commit fa0a327dd6
10 changed files with 642 additions and 673 deletions

View file

@ -40,21 +40,6 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode =
elif (c.p.nestedLoopCounter <= 0) and (c.p.nestedBlockCounter <= 0):
localError(n.info, errInvalidControlFlowX,
renderTree(n, {renderNoComments}))
proc semBlock(c: PContext, n: PNode): PNode =
result = n
Inc(c.p.nestedBlockCounter)
checkSonsLen(n, 2)
openScope(c.tab) # BUGFIX: label is in the scope of block!
if n.sons[0].kind != nkEmpty:
var labl = newSymG(skLabel, n.sons[0], c)
if sfGenSym notin labl.flags:
addDecl(c, labl)
n.sons[0] = newSymNode(labl, n.sons[0].info)
suggestSym(n.sons[0], labl)
n.sons[1] = semStmt(c, n.sons[1])
closeScope(c.tab)
Dec(c.p.nestedBlockCounter)
proc semAsm(con: PContext, n: PNode): PNode =
checkSonsLen(n, 2)
@ -79,20 +64,113 @@ proc toCover(t: PType): biggestInt =
else:
result = lengthOrd(skipTypes(t, abstractVar-{tyTypeDesc}))
proc semCase(c: PContext, n: PNode): PNode =
# check selector:
proc performProcvarCheck(c: PContext, n: PNode, s: PSym) =
var smoduleId = getModule(s).id
if sfProcVar notin s.flags and s.typ.callConv == ccDefault and
smoduleId != c.module.id and smoduleId != c.friendModule.id:
LocalError(n.info, errXCannotBePassedToProcVar, s.name.s)
proc semProcvarCheck(c: PContext, n: PNode) =
let n = n.skipConv
if n.kind == nkSym and n.sym.kind in {skProc, skMethod, skIterator,
skConverter}:
performProcvarCheck(c, n, n.sym)
proc semProc(c: PContext, n: PNode): PNode
include semdestruct
proc semDestructorCheck(c: PContext, n: PNode, flags: TExprFlags) {.inline.} =
if efAllowDestructor notin flags and n.kind in nkCallKinds+{nkObjConstr}:
if instantiateDestructor(c, n.typ):
LocalError(n.info, errGenerated,
"usage of a type with a destructor in a non destructible context")
proc newDeref(n: PNode): PNode {.inline.} =
result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0])
addSon(result, n)
proc semExprBranch(c: PContext, n: PNode): PNode =
result = semExpr(c, n)
if result.typ != nil:
# XXX tyGenericInst here?
semProcvarCheck(c, result)
if result.typ.kind == tyVar: result = newDeref(result)
semDestructorCheck(c, result, {})
proc semExprBranchScope(c: PContext, n: PNode): PNode =
openScope(c.tab)
result = semExprBranch(c, n)
closeScope(c.tab)
proc ImplicitlyDiscardable(n: PNode): bool =
result = isCallExpr(n) and n.sons[0].kind == nkSym and
sfDiscardable in n.sons[0].sym.flags
proc fixNilType(n: PNode) =
if isAtom(n):
if n.kind != nkNilLit and n.typ != nil:
localError(n.info, errDiscardValue)
else:
for it in n: fixNilType(it)
n.typ = nil
proc discardCheck(result: PNode) =
if result.typ != nil and result.typ.kind notin {tyStmt, tyEmpty}:
if result.kind == nkNilLit:
# XXX too much work and fixing would break bootstrapping:
#Message(n.info, warnNilStatement)
result.typ = nil
elif not ImplicitlyDiscardable(result) and result.typ.kind != tyError and
gCmd != cmdInteractive:
if result.typ.kind == tyNil:
fixNilType(result)
else:
localError(result.info, errDiscardValue)
proc semIf(c: PContext, n: PNode): PNode =
result = n
var typ = CommonTypeBegin
var hasElse = false
for i in countup(0, sonsLen(n) - 1):
var it = n.sons[i]
if it.len == 2:
when newScopeForIf: openScope(c.tab)
it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0]))
when not newScopeForIf: openScope(c.tab)
it.sons[1] = semExprBranch(c, it.sons[1])
typ = commonType(typ, it.sons[1].typ)
closeScope(c.tab)
elif it.len == 1:
hasElse = true
it.sons[0] = semExprBranchScope(c, it.sons[0])
typ = commonType(typ, it.sons[0].typ)
else: illFormedAst(it)
if isEmptyType(typ) or not hasElse:
for it in n: discardCheck(it.lastSon)
result.kind = nkIfStmt
else:
for it in n:
let j = it.len-1
it.sons[j] = fitNode(c, typ, it.sons[j])
result.kind = nkIfExpr
result.typ = typ
proc semCase(c: PContext, n: PNode): PNode =
result = n
checkMinSonsLen(n, 2)
openScope(c.tab)
n.sons[0] = semExprWithType(c, n.sons[0])
var chckCovered = false
var covered: biggestint = 0
var typ = CommonTypeBegin
var hasElse = false
case skipTypes(n.sons[0].Typ, abstractVarRange-{tyTypeDesc}).Kind
of tyInt..tyInt64, tyChar, tyEnum:
of tyInt..tyInt64, tyChar, tyEnum:
chckCovered = true
of tyFloat..tyFloat128, tyString, tyError:
of tyFloat..tyFloat128, tyString, tyError:
nil
else:
else:
LocalError(n.info, errSelectorMustBeOfCertainTypes)
return
for i in countup(1, sonsLen(n) - 1):
@ -101,21 +179,81 @@ proc semCase(c: PContext, n: PNode): PNode =
of nkOfBranch:
checkMinSonsLen(x, 2)
semCaseBranch(c, n, x, i, covered)
var length = sonsLen(x)
x.sons[length - 1] = semStmtScope(c, x.sons[length - 1])
of nkElifBranch:
var last = sonsLen(x)-1
x.sons[last] = semExprBranchScope(c, x.sons[last])
typ = commonType(typ, x.sons[last].typ)
of nkElifBranch:
chckCovered = false
checkSonsLen(x, 2)
when newScopeForIf: openScope(c.tab)
x.sons[0] = forceBool(c, semExprWithType(c, x.sons[0]))
x.sons[1] = semStmtScope(c, x.sons[1])
of nkElse:
when not newScopeForIf: openScope(c.tab)
x.sons[1] = semExprBranch(c, x.sons[1])
typ = commonType(typ, x.sons[1].typ)
closeScope(c.tab)
of nkElse:
chckCovered = false
checkSonsLen(x, 1)
x.sons[0] = semStmtScope(c, x.sons[0])
else: illFormedAst(x)
x.sons[0] = semExprBranchScope(c, x.sons[0])
typ = commonType(typ, x.sons[0].typ)
hasElse = true
else:
illFormedAst(x)
if chckCovered and (covered != toCover(n.sons[0].typ)):
localError(n.info, errNotAllCasesCovered)
closeScope(c.tab)
if isEmptyType(typ) or not hasElse:
for i in 1..n.len-1: discardCheck(n.sons[i].lastSon)
else:
for i in 1..n.len-1:
var it = n.sons[i]
let j = it.len-1
it.sons[j] = fitNode(c, typ, it.sons[j])
result.typ = typ
proc semTry(c: PContext, n: PNode): PNode =
result = n
inc c.p.inTryStmt
checkMinSonsLen(n, 2)
var typ = CommonTypeBegin
n.sons[0] = semExprBranchScope(c, n.sons[0])
typ = commonType(typ, n.sons[0].typ)
var check = initIntSet()
for i in countup(1, sonsLen(n) - 1):
var a = n.sons[i]
checkMinSonsLen(a, 1)
var length = sonsLen(a)
if a.kind == nkExceptBranch:
# XXX what does this do? so that ``except [a, b, c]`` is supported?
if length == 2 and a.sons[0].kind == nkBracket:
a.sons[0..0] = a.sons[0].sons
length = a.sonsLen
for j in countup(0, length-2):
var typ = semTypeNode(c, a.sons[j], nil)
if typ.kind == tyRef: typ = typ.sons[0]
if typ.kind != tyObject:
LocalError(a.sons[j].info, errExprCannotBeRaised)
a.sons[j] = newNodeI(nkType, a.sons[j].info)
a.sons[j].typ = typ
if ContainsOrIncl(check, typ.id):
localError(a.sons[j].info, errExceptionAlreadyHandled)
elif a.kind != nkFinally:
illFormedAst(n)
# last child of an nkExcept/nkFinally branch is a statement:
a.sons[length-1] = semExprBranchScope(c, a.sons[length-1])
typ = commonType(typ, a.sons[length-1].typ)
dec c.p.inTryStmt
if isEmptyType(typ):
discardCheck(n.sons[0])
for i in 1..n.len-1: discardCheck(n.sons[i].lastSon)
else:
n.sons[0] = fitNode(c, typ, n.sons[0])
for i in 1..n.len-1:
var it = n.sons[i]
let j = it.len-1
it.sons[j] = fitNode(c, typ, it.sons[j])
result.typ = typ
proc fitRemoveHiddenConv(c: PContext, typ: Ptype, n: PNode): PNode =
result = fitNode(c, typ, n)
@ -438,10 +576,6 @@ proc semForVars(c: PContext, n: PNode): PNode =
n.sons[length-1] = SemStmt(c, n.sons[length-1])
Dec(c.p.nestedLoopCounter)
proc newDeref(n: PNode): PNode {.inline.} =
result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0])
addSon(result, n)
proc implicitIterator(c: PContext, it: string, arg: PNode): PNode =
result = newNodeI(nkCall, arg.info)
result.add(newIdentNode(it.getIdent, arg.info))
@ -489,36 +623,6 @@ proc semRaise(c: PContext, n: PNode): PNode =
if typ.kind != tyRef or typ.sons[0].kind != tyObject:
localError(n.info, errExprCannotBeRaised)
proc semTry(c: PContext, n: PNode): PNode =
result = n
inc c.p.inTryStmt
checkMinSonsLen(n, 2)
n.sons[0] = semStmtScope(c, n.sons[0])
var check = initIntSet()
for i in countup(1, sonsLen(n) - 1):
var a = n.sons[i]
checkMinSonsLen(a, 1)
var length = sonsLen(a)
if a.kind == nkExceptBranch:
if length == 2 and a.sons[0].kind == nkBracket:
a.sons[0..0] = a.sons[0].sons
length = a.sonsLen
for j in countup(0, length - 2):
var typ = semTypeNode(c, a.sons[j], nil)
if typ.kind == tyRef: typ = typ.sons[0]
if typ.kind != tyObject:
LocalError(a.sons[j].info, errExprCannotBeRaised)
a.sons[j] = newNodeI(nkType, a.sons[j].info)
a.sons[j].typ = typ
if ContainsOrIncl(check, typ.id):
localError(a.sons[j].info, errExceptionAlreadyHandled)
elif a.kind != nkFinally:
illFormedAst(n)
# last child of an nkExcept/nkFinally branch is a statement:
a.sons[length - 1] = semStmtScope(c, a.sons[length - 1])
dec c.p.inTryStmt
proc addGenericParamListToScope(c: PContext, n: PNode) =
if n.kind != nkGenericParams: illFormedAst(n)
for i in countup(0, sonsLen(n)-1):
@ -746,22 +850,6 @@ proc activate(c: PContext, n: PNode) =
else:
nil
proc instantiateDestructor*(c: PContext, typ: PType): bool
proc doDestructorStuff(c: PContext, s: PSym, n: PNode) =
let t = s.typ.sons[1].skipTypes({tyVar})
t.destructor = s
# automatically insert calls to base classes' destructors
if n.sons[bodyPos].kind != nkEmpty:
for i in countup(0, t.sonsLen - 1):
# when inheriting directly from object
# there will be a single nil son
if t.sons[i] == nil: continue
if instantiateDestructor(c, t.sons[i]):
n.sons[bodyPos].addSon(newNode(nkCall, t.sym.info, @[
useSym(t.sons[i].destructor),
n.sons[paramsPos][1][0]]))
proc maybeAddResult(c: PContext, s: PSym, n: PNode) =
if s.typ.sons[0] != nil and
(s.kind != skIterator or s.typ.callConv == ccClosure):
@ -972,196 +1060,7 @@ proc semStaticStmt(c: PContext, n: PNode): PNode =
result = newNodeI(nkDiscardStmt, n.info, 1)
result.sons[0] = emptyNode
# special marker values that indicates that we are
# 1) AnalyzingDestructor: currently analyzing the type for destructor
# generation (needed for recursive types)
# 2) DestructorIsTrivial: completed the analysis before and determined
# that the type has a trivial destructor
var AnalyzingDestructor, DestructorIsTrivial: PSym
new(AnalyzingDestructor)
new(DestructorIsTrivial)
var
destructorName = getIdent"destroy_"
destructorParam = getIdent"this_"
destructorPragma = newIdentNode(getIdent"destructor", UnknownLineInfo())
rangeDestructorProc*: PSym
proc destroyField(c: PContext, field: PSym, holder: PNode): PNode =
if instantiateDestructor(c, field.typ):
result = newNode(nkCall, field.info, @[
useSym(field.typ.destructor),
newNode(nkDotExpr, field.info, @[holder, useSym(field)])])
proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode =
var nonTrivialFields = 0
result = newNode(nkCaseStmt, n.info, @[])
# case x.kind
result.addSon(newNode(nkDotExpr, n.info, @[holder, n.sons[0]]))
for i in countup(1, n.len - 1):
# of A, B:
var caseBranch = newNode(n[i].kind, n[i].info, n[i].sons[0 .. -2])
let recList = n[i].lastSon
var destroyRecList = newNode(nkStmtList, n[i].info, @[])
template addField(f: expr): stmt =
let stmt = destroyField(c, f, holder)
if stmt != nil:
destroyRecList.addSon(stmt)
inc nonTrivialFields
case recList.kind
of nkSym:
addField(recList.sym)
of nkRecList:
for j in countup(0, recList.len - 1):
addField(recList[j].sym)
else:
internalAssert false
caseBranch.addSon(destroyRecList)
result.addSon(caseBranch)
# maybe no fields were destroyed?
if nonTrivialFields == 0:
result = nil
proc generateDestructor(c: PContext, t: PType): PNode =
## generate a destructor for a user-defined object or tuple type
## returns nil if the destructor turns out to be trivial
template addLine(e: expr): stmt =
if result == nil: result = newNode(nkStmtList)
result.addSon(e)
# XXX: This may be true for some C-imported types such as
# Tposix_spawnattr
if t.n == nil or t.n.sons == nil: return
internalAssert t.n.kind == nkRecList
let destructedObj = newIdentNode(destructorParam, UnknownLineInfo())
# call the destructods of all fields
for s in countup(0, t.n.sons.len - 1):
case t.n.sons[s].kind
of nkRecCase:
let stmt = destroyCase(c, t.n.sons[s], destructedObj)
if stmt != nil: addLine(stmt)
of nkSym:
let stmt = destroyField(c, t.n.sons[s].sym, destructedObj)
if stmt != nil: addLine(stmt)
else:
internalAssert false
# base classes' destructors will be automatically called by
# semProcAux for both auto-generated and user-defined destructors
proc instantiateDestructor*(c: PContext, typ: PType): bool =
# returns true if the type already had a user-defined
# destructor or if the compiler generated a default
# member-wise one
var t = skipTypes(typ, {tyConst, tyMutable})
if t.destructor != nil:
# XXX: This is not entirely correct for recursive types, but we need
# it temporarily to hide the "destroy is already defined" problem
return t.destructor notin [AnalyzingDestructor, DestructorIsTrivial]
case t.kind
of tySequence, tyArray, tyArrayConstr, tyOpenArray, tyVarargs:
if instantiateDestructor(c, t.sons[0]):
if rangeDestructorProc == nil:
rangeDestructorProc = SymtabGet(c.tab, getIdent"nimDestroyRange")
t.destructor = rangeDestructorProc
return true
else:
return false
of tyTuple, tyObject:
t.destructor = AnalyzingDestructor
let generated = generateDestructor(c, t)
if generated != nil:
internalAssert t.sym != nil
var i = t.sym.info
let fullDef = newNode(nkProcDef, i, @[
newIdentNode(destructorName, i),
emptyNode,
emptyNode,
newNode(nkFormalParams, i, @[
emptyNode,
newNode(nkIdentDefs, i, @[
newIdentNode(destructorParam, i),
useSym(t.sym),
emptyNode]),
]),
newNode(nkPragma, i, @[destructorPragma]),
emptyNode,
generated
])
discard semProc(c, fullDef)
internalAssert t.destructor != nil
return true
else:
t.destructor = DestructorIsTrivial
return false
else:
return false
proc insertDestructors(c: PContext,
varSection: PNode): tuple[outer, inner: PNode] =
# Accepts a var or let section.
#
# When a var section has variables with destructors
# the var section is split up and finally blocks are inserted
# immediately after all "destructable" vars
#
# In case there were no destrucable variables, the proc returns
# (nil, nil) and the enclosing stmt-list requires no modifications.
#
# Otherwise, after the try blocks are created, the rest of the enclosing
# stmt-list should be inserted in the most `inner` such block (corresponding
# to the last variable).
#
# `outer` is a statement list that should replace the original var section.
# It will include the new truncated var section followed by the outermost
# try block.
let totalVars = varSection.sonsLen
for j in countup(0, totalVars - 1):
let
varId = varSection[j][0]
varTyp = varId.sym.typ
info = varId.info
if varTyp != nil and instantiateDestructor(c, varTyp) and
sfGlobal notin varId.sym.flags:
var tryStmt = newNodeI(nkTryStmt, info)
if j < totalVars - 1:
var remainingVars = newNodeI(varSection.kind, info)
remainingVars.sons = varSection.sons[(j+1)..(-1)]
let (outer, inner) = insertDestructors(c, remainingVars)
if outer != nil:
tryStmt.addSon(outer)
result.inner = inner
else:
result.inner = newNodeI(nkStmtList, info)
result.inner.addSon(remainingVars)
tryStmt.addSon(result.inner)
else:
result.inner = newNodeI(nkStmtList, info)
tryStmt.addSon(result.inner)
tryStmt.addSon(
newNode(nkFinally, info, @[
semStmt(c, newNode(nkCall, info, @[
useSym(varTyp.destructor),
useSym(varId.sym)]))]))
result.outer = newNodeI(nkStmtList, info)
varSection.sons.setLen(j+1)
result.outer.addSon(varSection)
result.outer.addSon(tryStmt)
return
proc ImplicitlyDiscardable(n: PNode): bool =
result = isCallExpr(n) and n.sons[0].kind == nkSym and
sfDiscardable in n.sons[0].sym.flags
var EnforceVoidContext = PType(kind: tyStmt)
proc semStmtList(c: PContext, n: PNode): PNode =
# these must be last statements in a block:
@ -1169,6 +1068,11 @@ proc semStmtList(c: PContext, n: PNode): PNode =
LastBlockStmts = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt}
result = n
var length = sonsLen(n)
var voidContext = false
var last = length-1
while last > 0 and n.sons[last].kind in {nkPragma, nkCommentStmt,
nkNilLit, nkEmpty}:
dec last
for i in countup(0, length - 1):
case n.sons[i].kind
of nkFinally, nkExceptBranch:
@ -1190,7 +1094,16 @@ proc semStmtList(c: PContext, n: PNode): PNode =
n.sons.setLen(i+1)
return
else:
n.sons[i] = semStmt(c, n.sons[i])
n.sons[i] = semExpr(c, n.sons[i])
if n.sons[i].typ == EnforceVoidContext:
voidContext = true
n.typ = EnforceVoidContext
elif i != last or voidContext:
discardCheck(n.sons[i])
else:
n.typ = n.sons[i].typ
if not isEmptyType(n.typ):
n.kind = nkStmtListExpr
case n.sons[i].kind
of nkVarSection, nkLetSection:
let (outer, inner) = insertDestructors(c, n.sons[i])
@ -1206,15 +1119,17 @@ proc semStmtList(c: PContext, n: PNode): PNode =
of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: nil
else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
else: nil
# a statement list (s; e) has the type 'e':
if result.kind == nkStmtList and result.len > 0:
var lastStmt = lastSon(result)
if lastStmt.kind != nkNilLit and not ImplicitlyDiscardable(lastStmt):
result.typ = lastStmt.typ
#localError(lastStmt.info, errGenerated,
# "Last expression must be explicitly returned if it " &
# "is discardable or discarded")
if result.len == 1:
result = result.sons[0]
when false:
# a statement list (s; e) has the type 'e':
if result.kind == nkStmtList and result.len > 0:
var lastStmt = lastSon(result)
if lastStmt.kind != nkNilLit and not ImplicitlyDiscardable(lastStmt):
result.typ = lastStmt.typ
#localError(lastStmt.info, errGenerated,
# "Last expression must be explicitly returned if it " &
# "is discardable or discarded")
proc SemStmt(c: PContext, n: PNode): PNode =
# now: simply an alias: