add default field support for object in ARC/ORC (#20480)

* 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>
This commit is contained in:
ringabout
2022-10-04 18:45:10 +08:00
committed by GitHub
parent 6cf0727192
commit f89ba2c951
23 changed files with 727 additions and 80 deletions

View File

@@ -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)
@@ -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

View File

@@ -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)

View File

@@ -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:
@@ -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:

View File

@@ -6,6 +6,7 @@ define:booting
define:nimcore
define:nimPreviewFloatRoundtrip
define:nimPreviewSlimSystem
define:nimPreviewRangeDefault
threads:off
#import:"$projectpath/testability"

View File

@@ -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)

View File

@@ -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) =

View File

@@ -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)

View File

@@ -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:
@@ -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.
@@ -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:
@@ -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])

View File

@@ -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:
@@ -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
@@ -85,15 +91,6 @@ proc semConstrField(c: PContext, flags: TExprFlags,
assignment.flags.incl nfSem
return initValue
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
return false
proc branchVals(c: PContext, caseNode: PNode, caseIdx: int,
isStmtBranch: bool): IntSet =
if caseNode[caseIdx].kind == nkOfBranch:
@@ -155,18 +152,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]
@@ -184,9 +177,9 @@ 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)
@@ -194,7 +187,7 @@ proc semConstructFields(c: PContext, n: PNode,
("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
@@ -206,7 +199,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:
@@ -289,13 +282,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)
@@ -308,33 +304,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]
@@ -378,7 +382,9 @@ proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) =
proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): 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")
@@ -409,7 +415,8 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
# 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

View File

@@ -612,6 +612,11 @@ 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 field = defaultNodeField(c, a[^2])
if field != nil:
a[^1] = field
field.flags.incl nfUseDefaultField
if a[^1].kind != nkEmpty:
def = semExprWithType(c, a[^1], {}, typ)
@@ -680,6 +685,12 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
addToVarSection(c, result, n, a)
continue
var v = semIdentDef(c, a[j], symkind, false)
if a[^1].kind != nkEmpty:
if {sfThread, sfNoInit} * v.flags != {} and
nfUseDefaultField in a[^1].flags:
a[^1] = c.graph.emptyNode
def = c.graph.emptyNode
a[^1].flags.excl nfUseDefaultField
styleCheckDef(c, v)
onDef(a[j].info, v)
if sfGenSym notin v.flags:

View File

@@ -23,7 +23,6 @@ const
errXExpectsOneTypeParam = "'$1' expects one type parameter"
errArrayExpectsTwoTypeParams = "array expects two type parameters"
errInvalidVisibilityX = "invalid visibility: '$1'"
errInitHereNotAllowed = "initialization not allowed here"
errXCannotBeAssignedTo = "'$1' cannot be assigned to"
errIteratorNotAllowed = "iterators can only be defined at the module's top level"
errXNeedsReturnType = "$1 needs a return type"
@@ -293,17 +292,18 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType =
if n.len == 2:
if isRange(n[1]):
result = semRangeAux(c, n[1], prev)
let n = result.n
if n[0].kind in {nkCharLit..nkUInt64Lit} and n[0].intVal > 0:
incl(result.flags, tfRequiresInit)
elif n[1].kind in {nkCharLit..nkUInt64Lit} and n[1].intVal < 0:
incl(result.flags, tfRequiresInit)
elif n[0].kind in {nkFloatLit..nkFloat64Lit} and
n[0].floatVal > 0.0:
incl(result.flags, tfRequiresInit)
elif n[1].kind in {nkFloatLit..nkFloat64Lit} and
n[1].floatVal < 0.0:
incl(result.flags, tfRequiresInit)
if not isDefined(c.config, "nimPreviewRangeDefault"):
let n = result.n
if n[0].kind in {nkCharLit..nkUInt64Lit} and n[0].intVal > 0:
incl(result.flags, tfRequiresInit)
elif n[1].kind in {nkCharLit..nkUInt64Lit} and n[1].intVal < 0:
incl(result.flags, tfRequiresInit)
elif n[0].kind in {nkFloatLit..nkFloat64Lit} and
n[0].floatVal > 0.0:
incl(result.flags, tfRequiresInit)
elif n[1].kind in {nkFloatLit..nkFloat64Lit} and
n[1].floatVal < 0.0:
incl(result.flags, tfRequiresInit)
else:
if n[1].kind == nkInfix and considerQuotedIdent(c, n[1][0]).s == "..<":
localError(c.config, n[0].info, "range types need to be constructed with '..', '..<' is not supported")
@@ -481,13 +481,19 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
var a = n[i]
if (a.kind != nkIdentDefs): illFormedAst(a, c.config)
checkMinSonsLen(a, 3, c.config)
if a[^2].kind != nkEmpty:
var hasDefaultField = a[^1].kind != nkEmpty
if hasDefaultField:
a[^1] = semConstExpr(c, a[^1])
typ = a[^1].typ
elif a[^2].kind != nkEmpty:
typ = semTypeNode(c, a[^2], nil)
if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange:
a[^1] = newIntNode(nkIntLit, firstOrd(c.config, typ))
a[^1].typ = typ
hasDefaultField = true
else:
localError(c.config, a.info, errTypeExpected)
typ = errorType(c)
if a[^1].kind != nkEmpty:
localError(c.config, a[^1].info, errInitHereNotAllowed)
for j in 0..<a.len - 2:
var field = newSymG(skField, a[j], c)
field.typ = typ
@@ -496,7 +502,11 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
if containsOrIncl(check, field.name.id):
localError(c.config, a[j].info, "attempt to redefine: '" & field.name.s & "'")
else:
result.n.add newSymNode(field)
let fSym = newSymNode(field)
if hasDefaultField:
fSym.sym.ast = a[^1]
fSym.sym.ast.flags.incl nfUseDefaultField
result.n.add fSym
addSonSkipIntLit(result, typ, c.idgen)
styleCheckDef(c, a[j].info, field)
onDef(field.info, field)
@@ -805,14 +815,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 c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange:
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
@@ -834,8 +851,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, f)
onDef(f.info, f)
if a.kind != nkEmpty: father.add a

View File

@@ -46,7 +46,6 @@ 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
contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break'
deferDetected, tooEarly: bool
graph: ModuleGraph
@@ -1061,6 +1060,13 @@ proc transform(c: PTransf, n: PNode): PNode =
result = n
of nkExceptBranch:
result = transformExceptBranch(c, n)
of nkObjConstr:
result = n
if result.typ.skipTypes({tyGenericInst, tyAlias, tySink}).kind == tyObject or
result.typ.skipTypes({tyGenericInst, tyAlias, tySink}).kind == tyRef and result.typ.skipTypes({tyGenericInst, tyAlias, tySink})[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:

View File

@@ -1181,7 +1181,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
c.gABx(n, opcLdNull, d, c.genType(n[1].typ))
c.gABx(n, opcNodeToReg, d, d)
c.genAsgnPatch(n[1], d)
of mDefault:
of mDefault, mZeroDefault:
if dest < 0: dest = c.getTemp(n.typ)
c.gABx(n, ldNullOpcode(n.typ), dest, c.genType(n.typ))
of mOf, mIs:

View File

@@ -22,6 +22,7 @@ hint[LineTooLong]=off
#hint[XDeclaredButNotUsed]=off
threads:on
define:nimPreviewRangeDefault
# Examples of how to setup a cross-compiler:
# Nim can target architectures and OSes different than the local host

View File

@@ -166,7 +166,7 @@ objectBranch = 'of' exprList colcom objectPart
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)
objectPart = IND{>} objectPart^+IND{=} DED

View File

@@ -24,6 +24,9 @@
include "system/basic_types"
func zeroDefault*[T](_: typedesc[T]): T {.magic: "ZeroDefault".} =
## returns the default value of the type `T`.
include "system/compilation"
{.push warning[GcMem]: off, warning[Uninit]: off.}
@@ -911,6 +914,7 @@ proc default*[T](_: typedesc[T]): T {.magic: "Default", noSideEffect.} =
# note: the doc comment also explains why `default` can't be implemented
# via: `template default*[T](t: typedesc[T]): T = (var v: T; v)`
proc reset*[T](obj: var T) {.noSideEffect.} =
## Resets an object `obj` to its default value.
obj = default(typeof(obj))
@@ -2753,3 +2757,8 @@ when notJSnotNims and not defined(nimSeqsV2):
moveMem(addr y[0], addr x[0], x.len)
assert y == "abcgh"
discard
proc arrayWith*[T](y: T, size: static int): array[size, T] {.noinit.} = # ? exempt from default value for result
## Creates a new array filled with `y`.
for i in 0..size-1:
result[i] = y

View File

@@ -125,6 +125,8 @@ proc setLen[T](s: var seq[T], newlen: Natural) =
if xu.p == nil or xu.p.cap < newlen:
xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, newlen - oldLen, sizeof(T), alignof(T)))
xu.len = newlen
for i in oldLen..<newlen:
xu.p.data[i] = default(T)
proc newSeq[T](s: var seq[T], len: Natural) =
shrink(s, 0)

View File

@@ -40,5 +40,6 @@ switch("define", "nimPreviewFloatRoundtrip")
switch("define", "nimPreviewDotLikeOps")
switch("define", "nimPreviewJsonutilsHoleyEnum")
switch("define", "nimPreviewHashRef")
switch("define", "nimPreviewRangeDefault")
when defined(windows):
switch("tlsEmulation", "off")

View File

@@ -1,5 +1,5 @@
discard """
cmd: "nim check $file"
cmd: "nim check --mm:refc $file"
errormsg: "'t' has unspecified generic parameters"
nimout: '''
t5167_5.nim(10, 16) Error: expression 'system' has no type (or is ambiguous)

View File

@@ -303,7 +303,7 @@ g = {},
h = {},
i = ["", "", ""],
j = @[],
k = 0,
k = -12,
l = [a = "",
b = @[]],
m = nil,

View File

@@ -0,0 +1,15 @@
type
Clean = object
mem: int
Default* = object
poi: int = 12
clc: Clean
se*: range[0'i32 .. high(int32)]
NonDefault* = object
poi: int
PrellDeque*[T] = object
pendingTasks*: range[0'i32 .. high(int32)]
head: T
tail: T

View File

@@ -0,0 +1,429 @@
discard """
matrix: "-d:nimPreviewRangeDefault --mm:refc; -d:nimPreviewRangeDefault --warningAsError:ProveInit --mm:orc"
targets: "c cpp js"
"""
import times
type
Guess = object
poi: DateTime
GuessDistinct = distinct Guess
block:
var x: Guess
discard Guess()
var y: GuessDistinct
discard y
discard GuessDistinct(x)
import mobject_default_value
block:
let x = Default()
doAssert x.se == 0'i32
# echo Default(poi: 12)
# echo Default(poi: 17)
# echo NonDefault(poi: 77)
block:
var x: Default
doAssert x.se == 0'i32
type
ObjectBase = object of RootObj
value = 12
ObjectBaseDistinct = distinct ObjectBase
DinstinctInObject = object
data: ObjectBaseDistinct
Object = object of ObjectBase
time: float = 1.2
date: int
scale: range[1..10]
Object2 = object
name: Object
Object3 = object
obj: Object2
ObjectTuple = tuple
base: ObjectBase
typ: int
obj: Object
TupleInObject = object
size = 777
data: ObjectTuple
Ref = ref object of ObjectBase
RefInt = ref object of Ref
data = 73
Ref2 = ref object of ObjectBase
RefInt2 = ref object of Ref
data = 73
var t {.threadvar.}: Default
# var m1, m2 {.threadvar.}: Default
block:
doAssert t.se == 0'i32
block: # ARC/ORC cannot bind destructors twice, so it cannot
# be moved into main
block:
var x: Ref
new(x)
doAssert x.value == 12, "Ref.value = " & $x.value
var y: RefInt
new(y)
doAssert y.value == 12
doAssert y.data == 73
block:
var x: Ref2
new(x, proc (x: Ref2) {.nimcall.} = discard "call Ref")
doAssert x.value == 12, "Ref.value = " & $x.value
proc call(x: RefInt2) =
discard "call RefInt"
var y: RefInt2
new(y, call)
doAssert y.value == 12
doAssert y.data == 73
template main {.dirty.} =
block: # bug #16744
type
R = range[1..10]
Obj = object
r: R
var
rVal: R # Works fine
objVal: Obj
doAssert rVal == 0 # it should be 1
doAssert objVal.r == 1
block: # bug #3608
type
abc = ref object
w: range[2..100]
proc createABC(): abc =
new(result)
result.w = 20
doAssert createABC().w == 20
block:
var x = new ObjectBase
doAssert x.value == 12
proc hello(): ref ObjectBase =
new result
let z = hello()
doAssert z.value == 12
block:
var base: ObjectBase
var x: ObjectBaseDistinct = ObjectBaseDistinct(base)
doAssert ObjectBase(x).value == 12
let y = ObjectBaseDistinct(default(ObjectBase))
doAssert ObjectBase(y).value == 12
proc hello(): ObjectBaseDistinct =
result = ObjectBaseDistinct(default(ObjectBase))
let z = hello()
doAssert ObjectBase(z).value == 12
block:
var x: DinstinctInObject
x.data = ObjectBaseDistinct(default(ObjectBase))
doAssert ObjectBase(x.data).value == 12
block:
var x: Object
doAssert x.value == 12
doAssert x.time == 1.2
doAssert x.scale == 1
let y = default(Object)
doAssert y.value == 12
doAssert y.time == 1.2
doAssert y.scale == 1
var x1, x2, x3: Object
doAssert x1.value == 12
doAssert x1.time == 1.2
doAssert x1.scale == 1
doAssert x2.value == 12
doAssert x2.time == 1.2
doAssert x2.scale == 1
doAssert x3.value == 12
doAssert x3.time == 1.2
doAssert x3.scale == 1
block:
var x = new Object
doAssert x[] == default(Object)
block:
var x: Object2
doAssert x.name.value == 12
doAssert x.name.time == 1.2
doAssert x.name.scale == 1
block:
var x: ref Object2
new x
doAssert x[] == default(Object2)
block:
var x: Object3
doAssert x.obj.name.value == 12
doAssert x.obj.name.time == 1.2
doAssert x.obj.name.scale == 1
when nimvm:
discard "fixme"
else:
when defined(gcArc) or defined(gcOrc):
block: #seq
var x = newSeq[Object](10)
let y = x[0]
doAssert y.value == 12
doAssert y.time == 1.2
doAssert y.scale == 1
block:
var x: seq[Object]
setLen(x, 5)
let y = x[^1]
doAssert y.value == 12
doAssert y.time == 1.2
doAssert y.scale == 1
block: # array
var x: array[10, Object]
let y = x[0]
doAssert y.value == 12
doAssert y.time == 1.2
doAssert y.scale == 1
block: # array
var x {.noinit.}: array[10, Object]
discard x
block: # tuple
var x: ObjectTuple
doAssert x.base.value == 12
doAssert x.typ == 0
doAssert x.obj.time == 1.2
doAssert x.obj.date == 0
doAssert x.obj.scale == 1
doAssert x.obj.value == 12
block: # tuple in object
var x: TupleInObject
doAssert x.data.base.value == 12
doAssert x.data.typ == 0
doAssert x.data.obj.time == 1.2
doAssert x.data.obj.date == 0
doAssert x.data.obj.scale == 1
doAssert x.data.obj.value == 12
doAssert x.size == 777
type
ObjectArray = object
data: array[10, Object]
block:
var x: ObjectArray
let y = x.data[0]
doAssert y.value == 12
doAssert y.time == 1.2
doAssert y.scale == 1
block:
var x: PrellDeque[int]
doAssert x.pendingTasks == 0
type
Color = enum
Red, Blue, Yellow
ObjectVarint = object
case kind: Color
of Red:
data: int = 10
of Blue:
fill = "123"
of Yellow:
time = 1.8'f32
ObjectVarint1 = object
case kind: Color = Blue
of Red:
data1: int = 10
of Blue:
fill2 = "123"
cry: float
of Yellow:
time3 = 1.8'f32
him: int
block:
var x = ObjectVarint(kind: Red)
doAssert x.kind == Red
doAssert x.data == 10
block:
var x = ObjectVarint(kind: Blue)
doAssert x.kind == Blue
doAssert x.fill == "123"
block:
var x = ObjectVarint(kind: Yellow)
doAssert x.kind == Yellow
doAssert typeof(x.time) is float32
block:
var x: ObjectVarint1
doAssert x.kind == Blue
doAssert x.fill2 == "123"
x.cry = 326
type
ObjectVarint2 = object
case kind: Color
of Red:
data: int = 10
of Blue:
fill = "123"
of Yellow:
time = 1.8'f32
block:
var x = ObjectVarint2(kind: Blue)
doAssert x.fill == "123"
block:
type
Color = enum
Red, Blue, Yellow
type
ObjectVarint3 = object # fixme it doesn't work with static
case kind: Color = Blue
of Red:
data1: int = 10
of Blue:
case name: Color = Blue
of Blue:
go = 12
else:
temp = 66
fill2 = "123"
cry: float
of Yellow:
time3 = 1.8'f32
him: int
block:
var x: ObjectVarint3
doAssert x.kind == Blue
doAssert x.name == Blue
doAssert x.go == 12
block:
var x = ObjectVarint3(kind: Blue, name: Red, temp: 99)
doAssert x.kind == Blue
doAssert x.name == Red
doAssert x.temp == 99
block:
type
Default = tuple
id: int = 1
obj: ObjectBase
name: string
Class = object
def: Default
Member = object
def: Default = (id: 777, obj: ObjectBase(), name: "fine")
block:
var x: Default
doAssert x.id == 1
doAssert x.obj == default(ObjectBase)
doAssert x.name == ""
block:
var x: Class
doAssert x.def == default(Default)
doAssert x.def.id == 1
doAssert x.def.obj == default(ObjectBase)
doAssert x.def.name == ""
when not defined(cpp):
block:
var x: Member
doAssert x.def.id == 777
doAssert x.def.obj == default(ObjectBase)
doAssert x.def.name == "fine"
block:
var x {.noinit.} = 12
doAssert x == 12
type
Pure = object
id: int = 12
var y {.noinit.}: Pure
doAssert y.id == 0
var z {.noinit.}: Pure = Pure(id: 77)
doAssert z.id == 77
proc main1 =
var my = @[1, 2, 3, 4, 5]
my.setLen(0)
my.setLen(5)
doAssert my == @[0, 0, 0, 0, 0]
proc main2 =
var my = "hello"
my.setLen(0)
my.setLen(5)
doAssert $(@my) == """@['\x00', '\x00', '\x00', '\x00', '\x00']"""
when defined(gcArc) or defined(gcOrc):
main1()
main2()
static: main()
main()

View File

@@ -1,4 +1,5 @@
discard """
disabled: "i386"
matrix: "-d:ssl"
"""