diff --git a/compiler/ast.nim b/compiler/ast.nim index 4174e7f34d3e..9727cfd549a7 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 f12023595f79..f71a4ddbd87a 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 301662f106fe..fd380e35caef 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 926cb4c28f6b..5156fa97116c 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 8b0a9d1c6450..9f489766583b 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 b6ee76cc9bf4..8f766f126c8e 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.. 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 0c6500408e69..2956ab4b65a0 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 48ea69648cc5..5cfde46f0007 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 77bd6b975d27..1463ba833243 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.. 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..= 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 dd0d546ebafe..5517eeacec98 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 a0bf6af566ed..117c1bc8457b 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 9f50a1f841d7..de525ae92387 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 878a4e5e1f2a..29a4300b6e60 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 1b6b3c9a8068..ed1a0077ed88 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 42d9938c5534..40fd50b48dc7 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..