Plug another hole: default(T) forbidden for objects requiring initialization

This commit is contained in:
Zahary Karadjov 2020-03-27 20:53:09 +02:00 • committed by Andreas Rumpf
commit 8ae48292ff
5 changed files with 31 additions and 19 deletions

View file

@ -46,6 +46,7 @@ proc addParams(c: PContext, n: PNode, kind: TSymKind)
proc maybeAddResult(c: PContext, s: PSym, n: PNode) proc maybeAddResult(c: PContext, s: PSym, n: PNode)
proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
proc activate(c: PContext, n: PNode) proc activate(c: PContext, n: PNode)
proc checkDefaultConstruction(c: PContext, typ: PType, info: TLineInfo)
proc semQuoteAst(c: PContext, n: PNode): PNode proc semQuoteAst(c: PContext, n: PNode): PNode
proc finishMethod(c: PContext, s: PSym) proc finishMethod(c: PContext, s: PSym)
proc evalAtCompileTime(c: PContext, n: PNode): PNode proc evalAtCompileTime(c: PContext, n: PNode): PNode
@ -58,24 +59,6 @@ proc isArrayConstr(n: PNode): bool {.inline.} =
result = n.kind == nkBracket and result = n.kind == nkBracket and
n.typ.skipTypes(abstractInst).kind == tyArray n.typ.skipTypes(abstractInst).kind == tyArray
type
ObjConstrContext = object
typ: PType # The constructed type
initExpr: PNode # The init expression (nkObjConstr)
requiresFullInit: bool # A `requiresInit` derived type will
# set this to true while visiting
# parent types.
InitStatus = enum # This indicates the result of object construction
initUnknown
initFull # All of the fields have been initialized
initPartial # Some of the fields have been initialized
initNone # None of the fields have been initialized
initConflict # Fields from different branches have been initialized
proc semConstructType(c: PContext, initExpr: PNode,
t: PType, flags: TExprFlags): InitStatus
template semIdeForTemplateOrGenericCheck(conf, n, requiresCheck) = template semIdeForTemplateOrGenericCheck(conf, n, requiresCheck) =
# we check quickly if the node is where the cursor is # we check quickly if the node is where the cursor is
when defined(nimsuggest): when defined(nimsuggest):

View file

@ -2264,6 +2264,12 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
of mSizeOf: of mSizeOf:
markUsed(c, n.info, s) markUsed(c, n.info, s)
result = semSizeof(c, setMs(n, s)) result = semSizeof(c, setMs(n, s))
of mDefault:
result = semDirectOp(c, n, flags)
c.config.internalAssert result[1].typ.kind == tyTypeDesc
let typ = result[1].typ.base
if typ.kind in {tyObject, tyTuple}:
checkDefaultConstruction(c, typ, n.info)
else: else:
result = semDirectOp(c, n, flags) result = semDirectOp(c, n, flags)

View file

@ -11,6 +11,21 @@
# included from sem.nim # included from sem.nim
type
ObjConstrContext = object
typ: PType # The constructed type
initExpr: PNode # The init expression (nkObjConstr)
requiresFullInit: bool # A `requiresInit` derived type will
# set this to true while visiting
# parent types.
InitStatus = enum # This indicates the result of object construction
initUnknown
initFull # All of the fields have been initialized
initPartial # Some of the fields have been initialized
initNone # None of the fields have been initialized
initConflict # Fields from different branches have been initialized
proc mergeInitStatus(existing: var InitStatus, newStatus: InitStatus) = proc mergeInitStatus(existing: var InitStatus, newStatus: InitStatus) =
case newStatus case newStatus
of initConflict: of initConflict:
@ -336,6 +351,9 @@ proc semConstructType(c: PContext, initExpr: PNode,
constrCtx.requiresFullInit = constrCtx.requiresFullInit or constrCtx.requiresFullInit = constrCtx.requiresFullInit or
tfRequiresInit in t.flags tfRequiresInit in t.flags
proc checkDefaultConstruction(c: PContext, typ: PType, info: TLineInfo) =
discard semConstructType(c, newNodeI(nkObjConstr, info), typ, {})
proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
var t = semTypeNode(c, n[0], nil) var t = semTypeNode(c, n[0], nil)
result = newNodeIT(nkObjConstr, n.info, t) result = newNodeIT(nkObjConstr, n.info, t)

View file

@ -616,7 +616,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
b[j] = newSymNode(v) b[j] = newSymNode(v)
if def.kind == nkEmpty: if def.kind == nkEmpty:
if v.typ.kind in {tyObject, tyTuple}: if v.typ.kind in {tyObject, tyTuple}:
discard semConstructType(c, newNodeI(nkObjConstr, v.info), v.typ, {}) checkDefaultConstruction(c, v.typ, v.info)
else: else:
checkNilable(c, v) checkNilable(c, v)
if sfCompileTime in v.flags: if sfCompileTime in v.flags:

View file

@ -125,12 +125,15 @@ accept PartialRequiresInit(a: 10, b: "x")
accept PartialRequiresInit(a: 20) accept PartialRequiresInit(a: 20)
reject PartialRequiresInit(b: "x") reject PartialRequiresInit(b: "x")
reject PartialRequiresInit() reject PartialRequiresInit()
reject default(PartialRequiresInit)
reject: reject:
var obj: PartialRequiresInit var obj: PartialRequiresInit
accept FullRequiresInit(a: 10, b: 20) accept FullRequiresInit(a: 10, b: 20)
reject FullRequiresInit(a: 10) reject FullRequiresInit(a: 10)
reject FullRequiresInit(b: 20) reject FullRequiresInit(b: 20)
reject FullRequiresInit()
reject default(FullRequiresInit)
reject: reject:
var obj: FullRequiresInit var obj: FullRequiresInit
@ -140,11 +143,13 @@ reject FullRequiresInitWithParent(a: notNilRef, b: nil, c: nil, e: 10, d: 20) #
reject FullRequiresInitWithParent(a: notNilRef, b: notNilRef, e: 10, d: 20) # c should not be missing reject FullRequiresInitWithParent(a: notNilRef, b: notNilRef, e: 10, d: 20) # c should not be missing
reject FullRequiresInitWithParent(a: notNilRef, b: notNilRef, c: nil, e: 10) # d should not be missing reject FullRequiresInitWithParent(a: notNilRef, b: notNilRef, c: nil, e: 10) # d should not be missing
reject FullRequiresInitWithParent() reject FullRequiresInitWithParent()
reject default(FullRequiresInitWithParent)
reject: reject:
var obj: FullRequiresInitWithParent var obj: FullRequiresInitWithParent
# this will be accepted, because the false outer branch will be taken and the inner A branch # this will be accepted, because the false outer branch will be taken and the inner A branch
accept TNestedChoices() accept TNestedChoices()
accept default(TNestedChoices)
accept: accept:
var obj: TNestedChoices var obj: TNestedChoices