Skip to content

Commit

Permalink
add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ringabout committed May 15, 2022
1 parent 8bfc396 commit 234f4c3
Show file tree
Hide file tree
Showing 10 changed files with 503 additions and 40 deletions.
12 changes: 12 additions & 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 Expand Up @@ -1453,6 +1454,17 @@ proc newIntTypeNode*(intVal: BiggestInt, typ: PType): PNode =
result.intVal = intVal
result.typ = typ

proc newFloatTypeNode*(floatVal: BiggestFloat, typ: PType): PNode =
let kind = skipTypes(typ, abstractVarRange).kind
case kind
of tyFloat32:
result = newNode(nkFloat32Lit)
of tyFloat64, tyFloat:
result = newNode(nkFloatLit)
else: doAssert false, $kind
result.floatVal = floatVal
result.typ = typ

proc newIntTypeNode*(intVal: Int128, typ: PType): PNode =
# XXX: introduce range check
newIntTypeNode(castToInt64(intVal), typ)
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
12 changes: 10 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,16 @@ 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({tyGenericInst, tyAlias, tySink}).kind == tyObject:
var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, result[^1].info, typ))
asgnExpr.typ = typ
var hasDefault: bool
asgnExpr.sons.add defaultFieldsForTheUninitialized(c.graph.config, typ.skipTypes({tyGenericInst, tyAlias, tySink}).n, hasDefault)
if hasDefault:
result = semObjConstr(c, asgnExpr, flags)
else:
result = semDirectOp(c, n, flags)

Expand Down
19 changes: 19 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,23 @@ 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({tyGenericInst, tyAlias, tySink}).kind == tyRef and typ.skipTypes({tyGenericInst, tyAlias, tySink})[0].kind == tyObject:
var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, result[^1].info, typ))
asgnExpr.typ = typ
var hasDefault: bool
var t = typ.skipTypes({tyGenericInst, tyAlias, tySink})[0]
while true:
asgnExpr.sons.add defaultFieldsForTheUninitialized(c.graph.config, t.n, hasDefault)
let base = t[0]
if base == nil:
break
t = skipTypes(base, skipPtrs)

if hasDefault: # todo apply default
result = newTree(nkAsgn, result[^1], asgnExpr)
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
70 changes: 43 additions & 27 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,17 @@ 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, _) = semConstructFields(c, innerRecords, constrCtx, flags) # todo
if status notin {initNone, initUnknown}:
mergeInitStatus(result, status)
result.status.mergeInitStatus status
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 +208,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 @@ -289,13 +291,16 @@ proc semConstructFields(c: PContext, n: PNode,
else:
wrongBranchError(failedBranch)

let (_, defaults) = semConstructFields(c, branchNode[^1], constrCtx, flags)
result.defaults.add defaults

# 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,33 +313,41 @@ 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:
let matchedBranch = n.pickCaseBranch discriminatorVal
if matchedBranch != nil: collectMissingFields matchedBranch
if matchedBranch != nil:
let (_, defaults) = semConstructFields(c, matchedBranch[^1], constrCtx, flags)
result.defaults.add defaults
collectMissingFields matchedBranch
else:
# 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 +391,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 +414,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
12 changes: 12 additions & 0 deletions compiler/semstmts.nim
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,15 @@ 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:
let m = copyTree(a[0])
# var v = semIdentDef(c, m, symkind, false)
# if {sfThread, sfNoInit} * v.flags != {}: # todo var m1, m2 {threadvar}
# discard # todo init threadvar properly
# else:
let field = defaultNodeField(c.graph.config, a[^2])
if field != nil:
a[^1] = field
if a[^1].kind != nkEmpty:
def = semExprWithType(c, a[^1], {})

Expand Down Expand Up @@ -662,6 +671,9 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
addToVarSection(c, result, n, a)
continue
var v = semIdentDef(c, a[j], symkind, false)
if {sfThread, sfNoInit} * v.flags != {}:
a[^1] = c.graph.emptyNode
def = c.graph.emptyNode
styleCheckDef(c.config, v)
onDef(a[j].info, v)
if sfGenSym notin v.flags:
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(abstractInst).kind in {tyRange, tyOrdinal}:
n[^1] = 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
Loading

0 comments on commit 234f4c3

Please sign in to comment.