Skip to content

Commit

Permalink
fix range type
Browse files Browse the repository at this point in the history
  • Loading branch information
ringabout committed May 6, 2022
1 parent 8bfc396 commit 0b32c96
Show file tree
Hide file tree
Showing 8 changed files with 164 additions and 39 deletions.
1 change: 1 addition & 0 deletions compiler/ast.nim
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,7 @@ type
nfLastRead # this node is a last read
nfFirstWrite# this node is a first write
nfHasComment # node has a comment
nfUseDefaultField

TNodeFlags* = set[TNodeFlag]
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 45)
Expand Down
6 changes: 1 addition & 5 deletions compiler/parser.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1963,11 +1963,7 @@ proc parseObjectCase(p: var Parser): PNode =
#| | IND{=} objectBranches)
result = newNodeP(nkRecCase, p)
getTokNoInd(p)
var a = newNodeP(nkIdentDefs, p)
a.add(identWithPragma(p))
eat(p, tkColon)
a.add(parseTypeDesc(p))
a.add(p.emptyNode)
var a = parseIdentColonEquals(p, {withPragma})
result.add(a)
if p.tok.tokType == tkColon: getTok(p)
flexComment(p, result)
Expand Down
10 changes: 8 additions & 2 deletions compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -897,8 +897,6 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
rawAddSon(typ, result.typ)
result.typ = typ

proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode

proc resolveIndirectCall(c: PContext; n, nOrig: PNode;
t: PType): TCandidate =
initCandidate(c, result, t)
Expand Down Expand Up @@ -2380,6 +2378,14 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
of mSizeOf:
markUsed(c, n.info, s)
result = semSizeof(c, setMs(n, s))
of mDefault:
result = semDirectOp(c, n, flags)
let typ = result[^1].typ.skipTypes({tyTypeDesc})
if typ.skipTypes(abstractInst).kind == tyObject:
var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, result[^1].info, typ))
asgnExpr.typ = typ
asgnExpr.sons.add defaultFieldsForTheUninitialized(c.graph.config, typ.skipTypes({tyGenericInst, tyAlias, tySink}).n)
result = semObjConstr(c, asgnExpr, flags)
else:
result = semDirectOp(c, n, flags)

Expand Down
10 changes: 10 additions & 0 deletions compiler/semmagic.nim
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
# This include file implements the semantic checking for magics.
# included from sem.nim

proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode

proc semAddrArg(c: PContext; n: PNode): PNode =
let x = semExprWithType(c, n)
if x.kind == nkSym:
Expand Down Expand Up @@ -512,6 +514,14 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
result = n
else:
result = plugin(c, n)
of mNew:
result = n
let typ = result[^1].typ
if typ.skipTypes(abstractInst).kind == tyRef and typ.skipTypes(abstractInst)[0].kind == tyObject:
var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, result[^1].info, typ))
asgnExpr.typ = typ
asgnExpr.sons.add defaultFieldsForTheUninitialized(c.graph.config, typ.skipTypes(abstractInst)[0].n)
result = newTree(nkAsgn, result[^1], semObjConstr(c, asgnExpr, flags))
of mNewFinalize:
# Make sure the finalizer procedure refers to a procedure
if n[^1].kind == nkSym and n[^1].sym.kind notin {skProc, skFunc}:
Expand Down
63 changes: 37 additions & 26 deletions compiler/semobjconstr.nim
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ type
initNone # None of the fields have been initialized
initConflict # Fields from different branches have been initialized


proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
flags: TExprFlags): tuple[status: InitStatus, defaults: seq[PNode]]

proc mergeInitStatus(existing: var InitStatus, newStatus: InitStatus) =
case newStatus
of initConflict:
Expand Down Expand Up @@ -72,7 +76,9 @@ proc semConstrField(c: PContext, flags: TExprFlags,
let assignment = locateFieldInInitExpr(c, field, initExpr)
if assignment != nil:
if nfSem in assignment.flags: return assignment[1]
if not fieldVisible(c, field):
if nfUseDefaultField in assignment[1].flags:
discard
elif not fieldVisible(c, field):
localError(c.config, initExpr.info,
"the field '$1' is not accessible." % [field.name.s])
return
Expand Down Expand Up @@ -155,18 +161,14 @@ proc collectMissingFields(c: PContext, fieldsRecList: PNode,
if assignment == nil:
constrCtx.missingFields.add r.sym


proc semConstructFields(c: PContext, n: PNode,
constrCtx: var ObjConstrContext,
flags: TExprFlags): InitStatus =
result = initUnknown

proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
flags: TExprFlags): tuple[status: InitStatus, defaults: seq[PNode]] =
case n.kind
of nkRecList:
for field in n:
let status = semConstructFields(c, field, constrCtx, flags)
mergeInitStatus(result, status)

let (subSt, subDf) = semConstructFields(c, field, constrCtx, flags)
result.status.mergeInitStatus subSt
result.defaults.add subDf
of nkRecCase:
template fieldsPresentInBranch(branchIdx: int): string =
let branch = n[branchIdx]
Expand All @@ -184,17 +186,18 @@ proc semConstructFields(c: PContext, n: PNode,

for i in 1..<n.len:
let innerRecords = n[i][^1]
let status = semConstructFields(c, innerRecords, constrCtx, flags)
let (status, defaults) = semConstructFields(c, innerRecords, constrCtx, flags)
if status notin {initNone, initUnknown}:
mergeInitStatus(result, status)
result.status.mergeInitStatus status
result.defaults.add defaults
if selectedBranch != -1:
let prevFields = fieldsPresentInBranch(selectedBranch)
let currentFields = fieldsPresentInBranch(i)
localError(c.config, constrCtx.initExpr.info,
("The fields '$1' and '$2' cannot be initialized together, " &
"because they are from conflicting branches in the case object.") %
[prevFields, currentFields])
result = initConflict
result.status = initConflict
else:
selectedBranch = i

Expand All @@ -206,7 +209,7 @@ proc semConstructFields(c: PContext, n: PNode,
("cannot prove that it's safe to initialize $1 with " &
"the runtime value for the discriminator '$2' ") %
[fields, discriminator.sym.name.s])
mergeInitStatus(result, initNone)
mergeInitStatus(result.status, initNone)

template wrongBranchError(i) =
if c.inUncheckedAssignSection == 0:
Expand Down Expand Up @@ -291,11 +294,11 @@ proc semConstructFields(c: PContext, n: PNode,

# When a branch is selected with a partial match, some of the fields
# that were not initialized may be mandatory. We must check for this:
if result == initPartial:
if result.status == initPartial:
collectMissingFields branchNode

else:
result = initNone
result.status = initNone
let discriminatorVal = semConstrField(c, flags + {efPreferStatic},
discriminator.sym,
constrCtx.initExpr)
Expand All @@ -308,7 +311,7 @@ proc semConstructFields(c: PContext, n: PNode,
let matchedBranch = n.pickCaseBranch defaultValue
collectMissingFields matchedBranch
else:
result = initPartial
result.status = initPartial
if discriminatorVal.kind == nkIntLit:
# When the discriminator is a compile-time value, we also know
# which branch will be selected:
Expand All @@ -318,23 +321,28 @@ proc semConstructFields(c: PContext, n: PNode,
# All bets are off. If any of the branches has a mandatory
# fields we must produce an error:
for i in 1..<n.len: collectMissingFields n[i]

of nkSym:
let field = n.sym
let e = semConstrField(c, flags, field, constrCtx.initExpr)
result = if e != nil: initFull else: initNone

if e != nil:
result.status = initFull
elif field.ast != nil:
result.status = initUnknown
result.defaults.add newTree(nkExprColonExpr, n, field.ast)
else:
result.status = initNone
else:
internalAssert c.config, false

proc semConstructTypeAux(c: PContext,
constrCtx: var ObjConstrContext,
flags: TExprFlags): InitStatus =
result = initUnknown
flags: TExprFlags): tuple[status: InitStatus, defaults: seq[PNode]] =
result.status = initUnknown
var t = constrCtx.typ
while true:
let status = semConstructFields(c, t.n, constrCtx, flags)
mergeInitStatus(result, status)
let (status, defaults) = semConstructFields(c, t.n, constrCtx, flags)
result.status.mergeInitStatus status
result.defaults.add defaults
if status in {initPartial, initNone, initUnknown}:
collectMissingFields c, t.n, constrCtx
let base = t[0]
Expand Down Expand Up @@ -378,7 +386,9 @@ proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) =
proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
var t = semTypeNode(c, n[0], nil)
result = newNodeIT(nkObjConstr, n.info, t)
for child in n: result.add child
result.add newNodeIT(nkType, n.info, t) #This will contain the default values to be added in transf
for i in 1..<n.len:
result.add n[i]

if t == nil:
return localErrorNode(c, result, "object constructor needs an object type")
Expand All @@ -399,7 +409,8 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
# field (if this is a case object, initialized fields in two different
# branches will be reported as an error):
var constrCtx = initConstrContext(t, result)
let initResult = semConstructTypeAux(c, constrCtx, flags)
let (initResult, defaults) = semConstructTypeAux(c, constrCtx, flags)
result[0].sons.add defaults
var hasError = false # needed to split error detect/report for better msgs

# It's possible that the object was not fully initialized while
Expand Down
25 changes: 25 additions & 0 deletions compiler/semstmts.nim
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,31 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
var typFlags: TTypeAllowedFlags

var def: PNode = c.graph.emptyNode
if a[^1].kind == nkEmpty and symkind == skVar and a[^2].typ != nil:
var v = semIdentDef(c, a[0], symkind, false)
if {sfThread, sfNoInit} * v.flags != {}: # todo var m1, m2 {threadvar}
discard # todo init threadvar properly
else:
let aTyp = a[^2].typ
if aTyp.skipTypes({tyGenericInst, tyAlias, tySink}).kind == tyObject:
var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, a[^2].info, aTyp))
asgnExpr.typ = aTyp
asgnExpr.sons.add defaultFieldsForTheUninitialized(c.graph.config, aTyp.skipTypes({tyGenericInst, tyAlias, tySink}).n)
a[^1] = asgnExpr
elif aTyp.kind == tyArray and aTyp[1].skipTypes({tyGenericInst, tyAlias, tySink}).kind == tyObject:
var asgnExpr = newNodeI(nkBracket, a[^2].info)
asgnExpr.typ = aTyp
let defaults = defaultFieldsForTheUninitialized(c.graph.config, aTyp[1].skipTypes({tyGenericInst, tyAlias, tySink}).n)
for i in 0..<toInt(lengthOrd(c.config, aTyp)):
var objExpr = newTree(nkObjConstr, newNodeIT(nkType, a[^2].info, aTyp[1]))
objExpr.typ = aTyp[1]
objExpr.sons.add defaults # todo create clean?
asgnExpr.add objExpr
a[^1] = asgnExpr
elif aTyp.kind == tyTuple:
# debug aTyp
discard

if a[^1].kind != nkEmpty:
def = semExprWithType(c, a[^1], {})

Expand Down
21 changes: 16 additions & 5 deletions compiler/semtypes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -777,14 +777,21 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
var a: PNode
if father.kind != nkRecList and n.len >= 4: a = newNodeI(nkRecList, n.info)
else: a = newNodeI(nkEmpty, n.info)
if n[^1].kind != nkEmpty:
localError(c.config, n[^1].info, errInitHereNotAllowed)
var typ: PType
if n[^2].kind == nkEmpty:
var hasDefaultField = n[^1].kind != nkEmpty
if hasDefaultField:
n[^1] = semConstExpr(c, n[^1])
typ = n[^1].typ
propagateToOwner(rectype, typ)
elif n[^2].kind == nkEmpty:
localError(c.config, n.info, errTypeExpected)
typ = errorType(c)
else:
typ = semTypeNode(c, n[^2], nil)
if typ.skipTypes({tyGenericInst, tyAlias, tySink}).kind in {tyRange, tyOrdinal}:
n[^1] = semConstExpr(c, newIntNode(nkIntLit, firstOrd(c.config, typ)))
n[^1].typ = typ
hasDefaultField = true
propagateToOwner(rectype, typ)
var fieldOwner = if c.inGenericContext > 0: c.getCurrOwner
else: rectype.sym
Expand All @@ -806,8 +813,12 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
inc(pos)
if containsOrIncl(check, f.name.id):
localError(c.config, info, "attempt to redefine: '" & f.name.s & "'")
if a.kind == nkEmpty: father.add newSymNode(f)
else: a.add newSymNode(f)
let fSym = newSymNode(f)
if hasDefaultField:
fSym.sym.ast = n[^1]
fSym.sym.ast.flags.incl nfUseDefaultField
if a.kind == nkEmpty: father.add fSym
else: a.add fSym
styleCheckDef(c.config, f)
onDef(f.info, f)
if a.kind != nkEmpty: father.add a
Expand Down
67 changes: 66 additions & 1 deletion compiler/transf.nim
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ type
module: PSym
transCon: PTransCon # top of a TransCon stack
inlining: int # > 0 if we are in inlining context (copy vars)
nestedProcs: int # > 0 if we are in a nested proc
genResult: bool
contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break'
deferDetected, tooEarly: bool
graph: ModuleGraph
Expand Down Expand Up @@ -927,6 +927,49 @@ proc commonOptimizations*(g: ModuleGraph; idgen: IdGenerator; c: PSym, n: PNode)
else:
result = n

proc defaultFieldsForTheUninitialized*(c: ConfigRef, recNode: PNode): seq[PNode] =
case recNode.kind
of nkRecList:
for field in recNode:
result.add defaultFieldsForTheUninitialized(c, field)
of nkRecCase:
discard
# let discriminator = recNode[0]
# var selectedBranch: int
# let defaultValue = discriminator.sym.ast
# if defaultValue == nil:
# # None of the branches were explicitly selected by the user and no value
# # was given to the discrimator. We can assume that it will be initialized
# # to zero and this will select a particular branch as a result:
# selectedBranch = recNode.pickCaseBranch newIntNode(nkIntLit#[c.graph]#, 0)
# else: # Try to use default value
# selectedBranch = recNode.pickCaseBranch defaultValue
# result.add newTree(nkExprColonExpr, discriminator, defaultValue)
# result.add defaultFieldsForTheUninitialized(recNode[selectedBranch][^1])
of nkSym:
let field = recNode.sym
if field.ast != nil: #Try to use default value
result.add newTree(nkExprColonExpr, recNode, field.ast)
elif recNode.typ.skipTypes({tyGenericInst, tyAlias, tySink}).kind == tyObject:
var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, recNode.info, recNode.typ))
asgnExpr.typ = recNode.typ
asgnExpr.flags.incl nfUseDefaultField
asgnExpr.sons.add defaultFieldsForTheUninitialized(c, recNode.typ.skipTypes({tyGenericInst, tyAlias, tySink}).n)
result.add newTree(nkExprColonExpr, recNode, asgnExpr)
elif recNode.typ.kind == tyArray and recNode.typ[1].skipTypes({tyGenericInst, tyAlias, tySink}).kind == tyObject:
var asgnExpr = newNodeI(nkBracket, recNode.info)
asgnExpr.typ = recNode.typ
asgnExpr.flags.incl nfUseDefaultField
let defaults = defaultFieldsForTheUninitialized(c, recNode.typ[1].skipTypes({tyGenericInst, tyAlias, tySink}).n)
for i in 0..<toInt(lengthOrd(c, recNode.typ)):
var objExpr = newTree(nkObjConstr, newNodeIT(nkType, recNode.info, recNode.typ[1]))
objExpr.typ = recNode.typ[1]
objExpr.sons.add defaults # todo create clean?
asgnExpr.add objExpr
result.add newTree(nkExprColonExpr, recNode, asgnExpr)
else:
assert false

proc transform(c: PTransf, n: PNode): PNode =
when false:
var oldDeferAnchor: PNode
Expand All @@ -935,6 +978,20 @@ proc transform(c: PTransf, n: PNode): PNode =
nkBlockStmt, nkBlockExpr}:
oldDeferAnchor = c.deferAnchor
c.deferAnchor = n

if c.genResult:
c.genResult = false
result = newNodeIT(nkStmtList, n.info, nil)
let toInit = c.getCurrOwner().ast[resultPos]
# todo should be same as var
if toInit.typ != nil and toInit.typ.skipTypes({tyGenericInst, tyAlias, tySink}).kind == tyObject:
var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, toInit.info, toInit.typ))
asgnExpr.typ = toInit.typ
asgnExpr.sons.add defaultFieldsForTheUninitialized(c.graph.config, toInit.typ.skipTypes({tyGenericInst, tyAlias, tySink}).n)
result.add transform(c, newTree(nkAsgn, toInit, asgnExpr))
result.add transform(c, n)
return result

case n.kind
of nkSym:
result = transformSym(c, n)
Expand Down Expand Up @@ -1052,6 +1109,13 @@ proc transform(c: PTransf, n: PNode): PNode =
result = n
of nkExceptBranch:
result = transformExceptBranch(c, n)
of nkObjConstr:
result = n
if result.typ.skipTypes(abstractInst).kind == tyObject or
result.typ.skipTypes(abstractInst).kind == tyRef and result.typ.skipTypes(abstractInst)[0].kind == tyObject:
result.sons.add result[0].sons
result[0] = newNodeIT(nkType, result.info, result.typ)
result = transformSons(c, result)
of nkCheckedFieldExpr:
result = transformSons(c, n)
if result[0].kind != nkDotExpr:
Expand Down Expand Up @@ -1079,6 +1143,7 @@ proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode =
# nodes into an empty node.
if nfTransf in n.flags: return n
pushTransCon(c, newTransCon(owner))
c.genResult = c.getCurrOwner().kind in routineKinds and c.transCon.owner.ast.len > resultPos and c.transCon.owner.ast[resultPos] != nil
result = transform(c, n)
popTransCon(c)
incl(result.flags, nfTransf)
Expand Down

0 comments on commit 0b32c96

Please sign in to comment.