Skip to content

Commit

Permalink
add default field support for object in ARC/ORC (#20480)
Browse files Browse the repository at this point in the history
* fresh start

* add cpp target

* add result support

* add nimPreviewRangeDefault

* reduce

* use orc

* refactor common parts

* add tuple support

* add testcase for tuple

* cleanup; fixes nimsuggest tests

* there is something wrong with cpp

* remove

* add support for seqs

* fixes style

* addd initial distinct support

* remove links

* typo

* fixes tuple defaults

* add rangedefault

* add cpp support

* fixes one more bugs

* add more hasDefaults

* fixes ordinal types

* add testcase for #16744

* add testcase for #3608

* fixes docgen

* small fix

* recursive

* fixes

* cleanup and remove tuple support

* fixes nimsuggest

* fixes generics procs

* refactor

* increases timeout

* refactor hasDefault

* zero default; disable i386

* add tuples back

* fixes bugs

* fixes tuple

* add more tests

* fix one more bug regarding tuples

* more tests and cleanup

* remove messy distinct types which must be initialized by original types

* add tests

* fixes zero default

* fixes grammar

* fixes tests

* fixes tests

* fixes tests

* fixes comments

* fixes and add testcase

* undo default values for results

Co-authored-by: flywind <43030857+xflywind@users.noreply.github.com>
  • Loading branch information
ringabout and ringabout authored Oct 4, 2022
1 parent 6cf0727 commit f89ba2c
Show file tree
Hide file tree
Showing 23 changed files with 727 additions and 80 deletions.
3 changes: 2 additions & 1 deletion compiler/ast.nim
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,7 @@ type
nfFirstWrite # this node is a first write
nfFirstWrite2 # alternative first write implementation
nfHasComment # node has a comment
nfUseDefaultField # node has a default value (object constructor)

TNodeFlags* = set[TNodeFlag]
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 45)
Expand Down Expand Up @@ -713,7 +714,7 @@ type
mInstantiationInfo, mGetTypeInfo, mGetTypeInfoV2,
mNimvm, mIntDefine, mStrDefine, mBoolDefine, mRunnableExamples,
mException, mBuiltinType, mSymOwner, mUncheckedArray, mGetImplTransf,
mSymIsInstantiationOf, mNodeId, mPrivateAccess
mSymIsInstantiationOf, mNodeId, mPrivateAccess, mZeroDefault


const
Expand Down
2 changes: 1 addition & 1 deletion compiler/ccgexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2572,7 +2572,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
p.module.s[cfsDynLibInit].addf("\t$1 = ($2) hcrGetProc($3, \"$1\");$n",
[mangleDynLibProc(prc), getTypeDesc(p.module, prc.loc.t), getModuleDllPath(p.module, prc)])
genCall(p, e, d)
of mDefault: genDefault(p, e, d)
of mDefault, mZeroDefault: genDefault(p, e, d)
of mReset: genReset(p, e)
of mEcho: genEcho(p, e[1].skipConv)
of mArrToSeq: genArrToSeq(p, e, d)
Expand Down
4 changes: 2 additions & 2 deletions compiler/jsgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2231,7 +2231,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
of mNewSeq: genNewSeq(p, n)
of mNewSeqOfCap: unaryExpr(p, n, r, "", "[]")
of mOf: genOf(p, n, r)
of mDefault: genDefault(p, n, r)
of mDefault, mZeroDefault: genDefault(p, n, r)
of mReset, mWasMoved: genReset(p, n)
of mEcho: genEcho(p, n, r)
of mNLen..mNError, mSlurp, mStaticExec:
Expand Down Expand Up @@ -2342,7 +2342,7 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
gen(p, val, a)
var f = it[0].sym
if f.loc.r == "": f.loc.r = mangleName(p.module, f)
fieldIDs.incl(lookupFieldAgain(nTyp, f).id)
fieldIDs.incl(lookupFieldAgain(n.typ.skipTypes({tyDistinct}), f).id)

let typ = val.typ.skipTypes(abstractInst)
if a.typ == etyBaseIndex:
Expand Down
1 change: 1 addition & 0 deletions compiler/nim.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ define:booting
define:nimcore
define:nimPreviewFloatRoundtrip
define:nimPreviewSlimSystem
define:nimPreviewRangeDefault
threads:off

#import:"$projectpath/testability"
Expand Down
8 changes: 2 additions & 6 deletions compiler/parser.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1960,16 +1960,12 @@ proc parseObjectCase(p: var Parser): PNode =
#| objectBranches = objectBranch (IND{=} objectBranch)*
#| (IND{=} 'elif' expr colcom objectPart)*
#| (IND{=} 'else' colcom objectPart)?
#| objectCase = 'case' identWithPragma ':' typeDesc ':'? COMMENT?
#| objectCase = 'case' declColonEquals ':'? COMMENT?
#| (IND{>} objectBranches DED
#| | 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
117 changes: 117 additions & 0 deletions compiler/sem.nim
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,123 @@ proc setGenericParamsMisc(c: PContext; n: PNode) =
else:
n[miscPos][1] = orig

proc caseBranchMatchesExpr(branch, matched: PNode): bool =
for i in 0 ..< branch.len-1:
if branch[i].kind == nkRange:
if overlap(branch[i], matched): return true
elif exprStructuralEquivalent(branch[i], matched):
return true

proc pickCaseBranchIndex(caseExpr, matched: PNode): int =
let endsWithElse = caseExpr[^1].kind == nkElse
for i in 1..<caseExpr.len - endsWithElse.int:
if caseExpr[i].caseBranchMatchesExpr(matched):
return i
if endsWithElse:
return caseExpr.len - 1

proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode): seq[PNode]
proc defaultNodeField(c: PContext, a: PNode): PNode
proc defaultNodeField(c: PContext, a: PNode, aTyp: PType): PNode

const defaultFieldsSkipTypes = {tyGenericInst, tyAlias, tySink}

proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool): seq[PNode] =
case recNode.kind
of nkRecList:
for field in recNode:
result.add defaultFieldsForTuple(c, field, hasDefault)
of nkSym:
let field = recNode.sym
let recType = recNode.typ.skipTypes(defaultFieldsSkipTypes)
if field.ast != nil: #Try to use default value
hasDefault = true
result.add newTree(nkExprColonExpr, recNode, field.ast)
else:
if recType.kind in {tyObject, tyArray, tyTuple}:
let asgnExpr = defaultNodeField(c, recNode, recNode.typ)
if asgnExpr != nil:
hasDefault = true
asgnExpr.flags.incl nfUseDefaultField
result.add newTree(nkExprColonExpr, recNode, asgnExpr)
return

let asgnType = newType(tyTypeDesc, nextTypeId(c.idgen), recType.owner)
rawAddSon(asgnType, recType)
let asgnExpr = newTree(nkCall,
newSymNode(getSysMagic(c.graph, recNode.info, "zeroDefault", mZeroDefault)),
newNodeIT(nkType, recNode.info, asgnType)
)
asgnExpr.flags.incl nfUseDefaultField
asgnExpr.typ = recType
result.add newTree(nkExprColonExpr, recNode, asgnExpr)
else:
doAssert false

proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode): seq[PNode] =
case recNode.kind
of nkRecList:
for field in recNode:
result.add defaultFieldsForTheUninitialized(c, field)
of nkRecCase:
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.pickCaseBranchIndex newIntNode(nkIntLit#[c.graph]#, 0)
else: # Try to use default value
selectedBranch = recNode.pickCaseBranchIndex defaultValue
result.add newTree(nkExprColonExpr, discriminator, defaultValue)
result.add defaultFieldsForTheUninitialized(c, recNode[selectedBranch][^1])
of nkSym:
let field = recNode.sym
let recType = recNode.typ.skipTypes(defaultFieldsSkipTypes)
if field.ast != nil: #Try to use default value
result.add newTree(nkExprColonExpr, recNode, field.ast)
elif recType.kind in {tyObject, tyArray, tyTuple}:
let asgnExpr = defaultNodeField(c, recNode, recNode.typ)
if asgnExpr != nil:
asgnExpr.flags.incl nfUseDefaultField
result.add newTree(nkExprColonExpr, recNode, asgnExpr)
else:
doAssert false

proc defaultNodeField(c: PContext, a: PNode, aTyp: PType): PNode =
let aTypSkip = aTyp.skipTypes(defaultFieldsSkipTypes)
if aTypSkip.kind == tyObject:
let child = defaultFieldsForTheUninitialized(c, aTyp.skipTypes(defaultFieldsSkipTypes).n)
if child.len > 0:
var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, a.info, aTyp))
asgnExpr.typ = aTyp
asgnExpr.sons.add child
result = semExpr(c, asgnExpr)
elif aTypSkip.kind == tyArray:
let child = defaultNodeField(c, a, aTypSkip[1])

if child != nil:
let node = newNode(nkIntLit)
node.intVal = toInt64(lengthOrd(c.graph.config, aTypSkip))
result = semExpr(c, newTree(nkCall, newSymNode(getSysSym(c.graph, a.info, "arrayWith"), a.info),
semExprWithType(c, child),
node
))
result.typ = aTyp
elif aTypSkip.kind == tyTuple:
var hasDefault = false
if aTypSkip.n != nil:
let children = defaultFieldsForTuple(c, aTypSkip.n, hasDefault)
if hasDefault and children.len > 0:
result = newNodeI(nkTupleConstr, a.info)
result.typ = aTyp
result.sons.add children
result = semExpr(c, result)

proc defaultNodeField(c: PContext, a: PNode): PNode =
result = defaultNodeField(c, a, a.typ)

include semtempl, semgnrc, semstmts, semexprs

proc addCodeForGenerics(c: PContext, n: PNode) =
Expand Down
2 changes: 0 additions & 2 deletions compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -926,8 +926,6 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
rawAddSon(typ, result.typ)
result.typ = typ

proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode

proc resolveIndirectCall(c: PContext; n, nOrig: PNode;
t: PType): TCandidate =
initCandidate(c, result, t)
Expand Down
43 changes: 37 additions & 6 deletions compiler/semmagic.nim
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,26 @@
# This include file implements the semantic checking for magics.
# included from sem.nim

proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode


proc addDefaultFieldForNew(c: PContext, n: PNode): PNode =
result = n
let typ = result[1].typ # new(x)
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 t = typ.skipTypes({tyGenericInst, tyAlias, tySink})[0]
while true:
asgnExpr.sons.add defaultFieldsForTheUninitialized(c, t.n)
let base = t[0]
if base == nil:
break
t = skipTypes(base, skipPtrs)

if asgnExpr.sons.len > 1:
result = newTree(nkAsgn, result[1], asgnExpr)

proc semAddrArg(c: PContext; n: PNode): PNode =
let x = semExprWithType(c, n)
if x.kind == nkSym:
Expand Down Expand Up @@ -494,13 +514,20 @@ proc semNewFinalize(c: PContext; n: PNode): PNode =
bindTypeHook(c, transFormedSym, n, attachedDestructor)
else:
bindTypeHook(c, turnFinalizerIntoDestructor(c, fin, n.info), n, attachedDestructor)
result = n
result = addDefaultFieldForNew(c, n)

proc semPrivateAccess(c: PContext, n: PNode): PNode =
let t = n[1].typ[0].toObjectFromRefPtrGeneric
c.currentScope.allowPrivateAccess.add t.sym
result = newNodeIT(nkEmpty, n.info, getSysType(c.graph, n.info, tyVoid))

proc checkDefault(c: PContext, n: PNode): PNode =
result = n
c.config.internalAssert result[1].typ.kind == tyTypeDesc
let constructed = result[1].typ.base
if constructed.requiresInit:
message(c.config, n.info, warnUnsafeDefault, typeToString(constructed))

proc magicsAfterOverloadResolution(c: PContext, n: PNode,
flags: TExprFlags): PNode =
## This is the preferred code point to implement magics.
Expand Down Expand Up @@ -559,6 +586,8 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
result = n
else:
result = plugin(c, n)
of mNew:
result = addDefaultFieldForNew(c, n)
of mNewFinalize:
result = semNewFinalize(c, n)
of mDestroy:
Expand Down Expand Up @@ -587,11 +616,13 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
if seqType.kind == tySequence and seqType.base.requiresInit:
message(c.config, n.info, warnUnsafeSetLen, typeToString(seqType.base))
of mDefault:
result = n
c.config.internalAssert result[1].typ.kind == tyTypeDesc
let constructed = result[1].typ.base
if constructed.requiresInit:
message(c.config, n.info, warnUnsafeDefault, typeToString(constructed))
result = checkDefault(c, n)
let typ = result[^1].typ.skipTypes({tyTypeDesc})
let defaultExpr = defaultNodeField(c, result[^1], typ)
if defaultExpr != nil:
result = defaultExpr
of mZeroDefault:
result = checkDefault(c, n)
of mIsolate:
if not checkIsolate(n[1]):
localError(c.config, n.info, "expression cannot be isolated: " & $n[1])
Expand Down
Loading

0 comments on commit f89ba2c

Please sign in to comment.