diff --git a/compiler/ast.nim b/compiler/ast.nim index 7b26703fe007..0a424afc6411 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -505,6 +505,7 @@ type nfLastRead # this node is a last read nfFirstWrite# this node is a first write 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) @@ -707,7 +708,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 8f5a667951c4..f85407e523e8 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2465,7 +2465,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 3775e597428e..bc76976dcf5b 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -2162,7 +2162,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: @@ -2272,7 +2272,7 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = gen(p, val, a) var f = it[0].sym if f.loc.r == nil: f.loc.r = mangleName(p.module, f) - fieldIDs.incl(lookupFieldAgain(n.typ, 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 d87febc17929..763c9259b525 100644 --- a/compiler/nim.cfg +++ b/compiler/nim.cfg @@ -5,6 +5,7 @@ hint[XDeclaredButNotUsed]:off define:booting define:nimcore define:nimPreviewFloatRoundtrip +define:nimPreviewRangeDefault #import:"$projectpath/testability" diff --git a/compiler/parser.nim b/compiler/parser.nim index 82d2ad7d2019..b7539cd64305 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -2016,16 +2016,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 4531947dc2bb..416d7852220c 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 b7037d8d1d5e..929525baf0d3 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -927,8 +927,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 49c64086caa4..d9996ebd43d2 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; isUnsafeAddr = false): PNode = let x = semExprWithType(c, n) if x.kind == nkSym: @@ -463,6 +483,13 @@ proc semPrivateAccess(c: PContext, n: PNode): PNode = 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; expectedType: PType = nil): PNode = ## This is the preferred code point to implement magics. @@ -521,6 +548,8 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, result = n else: result = plugin(c, n) + of mNew: + result = addDefaultFieldForNew(c, n) of mNewFinalize: # Make sure the finalizer procedure refers to a procedure if n[^1].kind == nkSym and n[^1].sym.kind notin {skProc, skFunc}: @@ -572,11 +601,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") @@ -486,13 +486,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 30b05e5a3fc7..769c367ebf90 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -43,7 +43,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 isIntroducingNewLocalVars: bool # true if we are in `introducingNewLocalVars` (don't transform yields) contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break' deferDetected, tooEarly: bool @@ -1052,6 +1051,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 72bcc56fa75d..13c9b0948201 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1206,7 +1206,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = c.gABx(n, opcLdNull, d, c.genType(n[1].typ)) c.gABC(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 335d84ddd417..8a15e2c6715b 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -16,6 +16,8 @@ cc = gcc hint[LineTooLong]=off #hint[XDeclaredButNotUsed]=off +define:nimPreviewRangeDefault + # Examples of how to setup a cross-compiler: # Cross-compiling for Raspberry Pi. diff --git a/doc/grammar.txt b/doc/grammar.txt index dad457641408..e97bac30981a 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 83052930a343..5c54fa8f5c22 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -111,6 +111,10 @@ proc compileOption*(option, arg: string): bool {. when compileOption("opt", "size") and compileOption("gc", "boehm"): discard "compiled with optimization for size and uses Boehm's GC" +func zeroDefault*[T](_: typedesc[T]): T {.magic: "ZeroDefault".} = + ## returns the default value of the type `T`. + + {.push warning[GcMem]: off, warning[Uninit]: off.} # {.push hints: off.} @@ -997,6 +1001,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)) @@ -3146,3 +3151,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 e12dada7503f..a4feb8e8a354 100644 --- a/lib/system/seqs_v2.nim +++ b/lib/system/seqs_v2.nim @@ -124,6 +124,8 @@ proc setLen[T](s: var seq[T], newlen: Natural) = if xu.p == nil or (xu.p.cap and not strlitFlag) < newlen: xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, newlen - oldLen, sizeof(T), alignof(T))) xu.len = newlen + for i in oldLen..