diff options
-rw-r--r-- | compiler/ast.nim | 3 | ||||
-rw-r--r-- | compiler/ccgexprs.nim | 2 | ||||
-rw-r--r-- | compiler/jsgen.nim | 4 | ||||
-rw-r--r-- | compiler/nim.cfg | 1 | ||||
-rw-r--r-- | compiler/parser.nim | 8 | ||||
-rw-r--r-- | compiler/sem.nim | 117 | ||||
-rw-r--r-- | compiler/semexprs.nim | 2 | ||||
-rw-r--r-- | compiler/semmagic.nim | 43 | ||||
-rw-r--r-- | compiler/semobjconstr.nim | 79 | ||||
-rw-r--r-- | compiler/semstmts.nim | 11 | ||||
-rw-r--r-- | compiler/semtypes.nim | 63 | ||||
-rw-r--r-- | compiler/transf.nim | 8 | ||||
-rw-r--r-- | compiler/vmgen.nim | 2 | ||||
-rw-r--r-- | config/nim.cfg | 1 | ||||
-rw-r--r-- | doc/grammar.txt | 2 | ||||
-rw-r--r-- | lib/system.nim | 9 | ||||
-rw-r--r-- | lib/system/seqs_v2.nim | 2 | ||||
-rw-r--r-- | tests/config.nims | 1 | ||||
-rw-r--r-- | tests/errmsgs/t5167_5.nim | 2 | ||||
-rw-r--r-- | tests/js/trepr.nim | 2 | ||||
-rw-r--r-- | tests/objects/mobject_default_value.nim | 15 | ||||
-rw-r--r-- | tests/objects/tobject_default_value.nim | 429 | ||||
-rw-r--r-- | tests/stdlib/tnetconnect.nim | 1 |
23 files changed, 727 insertions, 80 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 4174e7f34..9727cfd54 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -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 diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index f12023595..f71a4ddbd 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -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) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 301662f10..fd380e35c 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -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: diff --git a/compiler/nim.cfg b/compiler/nim.cfg index 926cb4c28..5156fa971 100644 --- a/compiler/nim.cfg +++ b/compiler/nim.cfg @@ -6,6 +6,7 @@ define:booting define:nimcore define:nimPreviewFloatRoundtrip define:nimPreviewSlimSystem +define:nimPreviewRangeDefault threads:off #import:"$projectpath/testability" diff --git a/compiler/parser.nim b/compiler/parser.nim index 8b0a9d1c6..9f4897665 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -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) diff --git a/compiler/sem.nim b/compiler/sem.nim index b6ee76cc9..8f766f126 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -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) = diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 0c6500408..2956ab4b6 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -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) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 48ea69648..5cfde46f0 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -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]) diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index 77bd6b975..1463ba833 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -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 diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 26356a33c..72e18ec3e 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -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: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index b732eff9c..5dd622a07 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -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 diff --git a/compiler/transf.nim b/compiler/transf.nim index dd0d546eb..5517eeace 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -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: diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index a0bf6af56..117c1bc84 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -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: diff --git a/config/nim.cfg b/config/nim.cfg index 9f50a1f84..de525ae92 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -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 diff --git a/doc/grammar.txt b/doc/grammar.txt index 878a4e5e1..29a4300b6 100644 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -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 diff --git a/lib/system.nim b/lib/system.nim index 1b6b3c9a8..ed1a0077e 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -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 diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim index 42d9938c5..40fd50b48 100644 --- a/lib/system/seqs_v2.nim +++ b/lib/system/seqs_v2.nim @@ -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) diff --git a/tests/config.nims b/tests/config.nims index 5195ebcaf..4b5a5e3a2 100644 --- a/tests/config.nims +++ b/tests/config.nims @@ -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") diff --git a/tests/errmsgs/t5167_5.nim b/tests/errmsgs/t5167_5.nim index a9e260845..ab6f094f3 100644 --- a/tests/errmsgs/t5167_5.nim +++ b/tests/errmsgs/t5167_5.nim @@ -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) diff --git a/tests/js/trepr.nim b/tests/js/trepr.nim index c63b64d3a..047016aea 100644 --- a/tests/js/trepr.nim +++ b/tests/js/trepr.nim @@ -303,7 +303,7 @@ g = {}, h = {}, i = ["", "", ""], j = @[], -k = 0, +k = -12, l = [a = "", b = @[]], m = nil, diff --git a/tests/objects/mobject_default_value.nim b/tests/objects/mobject_default_value.nim new file mode 100644 index 000000000..224549501 --- /dev/null +++ b/tests/objects/mobject_default_value.nim @@ -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 diff --git a/tests/objects/tobject_default_value.nim b/tests/objects/tobject_default_value.nim new file mode 100644 index 000000000..d826566fd --- /dev/null +++ b/tests/objects/tobject_default_value.nim @@ -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() diff --git a/tests/stdlib/tnetconnect.nim b/tests/stdlib/tnetconnect.nim index 3a80df19f..0fff8fdce 100644 --- a/tests/stdlib/tnetconnect.nim +++ b/tests/stdlib/tnetconnect.nim @@ -1,4 +1,5 @@ discard """ + disabled: "i386" matrix: "-d:ssl" """ |