First steps, the compiler can boot with enforced requiresInit
This commit is contained in:
parent
216fd59c44
commit
a8b6222c86
7 changed files with 26 additions and 29 deletions
|
|
@ -517,8 +517,10 @@ type
|
||||||
tfPartial, # type is declared as 'partial'
|
tfPartial, # type is declared as 'partial'
|
||||||
tfNotNil, # type cannot be 'nil'
|
tfNotNil, # type cannot be 'nil'
|
||||||
|
|
||||||
tfNeedsInit, # type constains a "not nil" constraint somewhere or some
|
tfHasRequiresInit,# type constains a "not nil" constraint somewhere or
|
||||||
# other type so that it requires initialization
|
# a `requiresInit` field, so the default zero init
|
||||||
|
# is not appropriate
|
||||||
|
tfRequiresInit, # all fields of the type must be initialized
|
||||||
tfVarIsPtr, # 'var' type is translated like 'ptr' even in C++ mode
|
tfVarIsPtr, # 'var' type is translated like 'ptr' even in C++ mode
|
||||||
tfHasMeta, # type contains "wildcard" sub-types such as generic params
|
tfHasMeta, # type contains "wildcard" sub-types such as generic params
|
||||||
# or other type classes
|
# or other type classes
|
||||||
|
|
@ -1484,11 +1486,11 @@ proc propagateToOwner*(owner, elem: PType; propagateHasAsgn = true) =
|
||||||
if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvocation}:
|
if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvocation}:
|
||||||
owner.flags.incl tfNotNil
|
owner.flags.incl tfNotNil
|
||||||
elif owner.kind notin HaveTheirOwnEmpty:
|
elif owner.kind notin HaveTheirOwnEmpty:
|
||||||
owner.flags.incl tfNeedsInit
|
owner.flags.incl tfHasRequiresInit
|
||||||
|
|
||||||
if tfNeedsInit in elem.flags:
|
if tfRequiresInit in elem.flags:
|
||||||
if owner.kind in HaveTheirOwnEmpty: discard
|
if owner.kind in HaveTheirOwnEmpty: discard
|
||||||
else: owner.flags.incl tfNeedsInit
|
else: owner.flags.incl tfHasRequiresInit
|
||||||
|
|
||||||
if elem.isMetaType:
|
if elem.isMetaType:
|
||||||
owner.flags.incl tfHasMeta
|
owner.flags.incl tfHasMeta
|
||||||
|
|
|
||||||
|
|
@ -1088,7 +1088,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||||
of wRequiresInit:
|
of wRequiresInit:
|
||||||
noVal(c, it)
|
noVal(c, it)
|
||||||
if sym.typ == nil: invalidPragma(c, it)
|
if sym.typ == nil: invalidPragma(c, it)
|
||||||
else: incl(sym.typ.flags, tfNeedsInit)
|
else: incl(sym.typ.flags, tfRequiresInit)
|
||||||
of wByRef:
|
of wByRef:
|
||||||
noVal(c, it)
|
noVal(c, it)
|
||||||
if sym == nil or sym.typ == nil:
|
if sym == nil or sym.typ == nil:
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,7 @@ proc fieldsPresentInInitExpr(c: PContext, fieldsRecList, initExpr: PNode): strin
|
||||||
|
|
||||||
proc missingMandatoryFields(c: PContext, fieldsRecList, initExpr: PNode): string =
|
proc missingMandatoryFields(c: PContext, fieldsRecList, initExpr: PNode): string =
|
||||||
for r in directFieldsInRecList(fieldsRecList):
|
for r in directFieldsInRecList(fieldsRecList):
|
||||||
if {tfNotNil, tfNeedsInit} * r.sym.typ.flags != {}:
|
if {tfNotNil, tfRequiresInit} * r.sym.typ.flags != {}:
|
||||||
let assignment = locateFieldInInitExpr(c, r.sym, initExpr)
|
let assignment = locateFieldInInitExpr(c, r.sym, initExpr)
|
||||||
if assignment == nil:
|
if assignment == nil:
|
||||||
if result.len == 0:
|
if result.len == 0:
|
||||||
|
|
@ -358,15 +358,10 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
|
|
||||||
# It's possible that the object was not fully initialized while
|
# It's possible that the object was not fully initialized while
|
||||||
# specifying a .requiresInit. pragma.
|
# specifying a .requiresInit. pragma.
|
||||||
# XXX: Turn this into an error in the next release
|
if tfRequiresInit in t.flags and initResult != initFull:
|
||||||
if tfNeedsInit in t.flags and initResult != initFull:
|
localError(c.config, n.info,
|
||||||
# XXX: Disable this warning for now, because tfNeedsInit is propagated
|
|
||||||
# too aggressively from fields to object types (and this is not correct
|
|
||||||
# in case objects)
|
|
||||||
when false: message(n.info, warnUser,
|
|
||||||
"object type uses the 'requiresInit' pragma, but not all fields " &
|
"object type uses the 'requiresInit' pragma, but not all fields " &
|
||||||
"have been initialized. future versions of Nim will treat this as " &
|
"have been initialized.")
|
||||||
"an error")
|
|
||||||
|
|
||||||
# Since we were traversing the object fields, it's possible that
|
# Since we were traversing the object fields, it's possible that
|
||||||
# not all of the fields specified in the constructor was visited.
|
# not all of the fields specified in the constructor was visited.
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,7 @@ proc initVar(a: PEffects, n: PNode; volatileCheck: bool) =
|
||||||
proc initVarViaNew(a: PEffects, n: PNode) =
|
proc initVarViaNew(a: PEffects, n: PNode) =
|
||||||
if n.kind != nkSym: return
|
if n.kind != nkSym: return
|
||||||
let s = n.sym
|
let s = n.sym
|
||||||
if {tfNeedsInit, tfNotNil} * s.typ.flags <= {tfNotNil}:
|
if {tfRequiresInit, tfNotNil} * s.typ.flags <= {tfNotNil}:
|
||||||
# 'x' is not nil, but that doesn't mean its "not nil" children
|
# 'x' is not nil, but that doesn't mean its "not nil" children
|
||||||
# are initialized:
|
# are initialized:
|
||||||
initVar(a, n, volatileCheck=true)
|
initVar(a, n, volatileCheck=true)
|
||||||
|
|
@ -253,7 +253,7 @@ proc useVar(a: PEffects, n: PNode) =
|
||||||
# If the variable is explicitly marked as .noinit. do not emit any error
|
# If the variable is explicitly marked as .noinit. do not emit any error
|
||||||
a.init.add s.id
|
a.init.add s.id
|
||||||
elif s.id notin a.init:
|
elif s.id notin a.init:
|
||||||
if {tfNeedsInit, tfNotNil} * s.typ.flags != {}:
|
if {tfRequiresInit, tfNotNil} * s.typ.flags != {}:
|
||||||
message(a.config, n.info, warnProveInit, s.name.s)
|
message(a.config, n.info, warnProveInit, s.name.s)
|
||||||
else:
|
else:
|
||||||
message(a.config, n.info, warnUninit, s.name.s)
|
message(a.config, n.info, warnUninit, s.name.s)
|
||||||
|
|
@ -838,7 +838,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||||
# may not look like an assignment, but it is:
|
# may not look like an assignment, but it is:
|
||||||
let arg = n[1]
|
let arg = n[1]
|
||||||
initVarViaNew(tracked, arg)
|
initVarViaNew(tracked, arg)
|
||||||
if arg.typ.len != 0 and {tfNeedsInit} * arg.typ.lastSon.flags != {}:
|
if arg.typ.len != 0 and {tfRequiresInit} * arg.typ.lastSon.flags != {}:
|
||||||
if a.sym.magic == mNewSeq and n[2].kind in {nkCharLit..nkUInt64Lit} and
|
if a.sym.magic == mNewSeq and n[2].kind in {nkCharLit..nkUInt64Lit} and
|
||||||
n[2].intVal == 0:
|
n[2].intVal == 0:
|
||||||
# var s: seq[notnil]; newSeq(s, 0) is a special case!
|
# var s: seq[notnil]; newSeq(s, 0) is a special case!
|
||||||
|
|
@ -1203,7 +1203,7 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
|
||||||
createTypeBoundOps(t, typ, param.info)
|
createTypeBoundOps(t, typ, param.info)
|
||||||
|
|
||||||
if not isEmptyType(s.typ[0]) and
|
if not isEmptyType(s.typ[0]) and
|
||||||
({tfNeedsInit, tfNotNil} * s.typ[0].flags != {} or
|
({tfRequiresInit, tfNotNil} * s.typ[0].flags != {} or
|
||||||
s.typ[0].skipTypes(abstractInst).kind == tyVar) and
|
s.typ[0].skipTypes(abstractInst).kind == tyVar) and
|
||||||
s.kind in {skProc, skFunc, skConverter, skMethod}:
|
s.kind in {skProc, skFunc, skConverter, skMethod}:
|
||||||
var res = s.ast[resultPos].sym # get result symbol
|
var res = s.ast[resultPos].sym # get result symbol
|
||||||
|
|
|
||||||
|
|
@ -336,7 +336,7 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym =
|
||||||
|
|
||||||
proc checkNilable(c: PContext; v: PSym) =
|
proc checkNilable(c: PContext; v: PSym) =
|
||||||
if {sfGlobal, sfImportc} * v.flags == {sfGlobal} and
|
if {sfGlobal, sfImportc} * v.flags == {sfGlobal} and
|
||||||
{tfNotNil, tfNeedsInit} * v.typ.flags != {}:
|
{tfNotNil, tfRequiresInit} * v.typ.flags != {}:
|
||||||
if v.astdef.isNil:
|
if v.astdef.isNil:
|
||||||
message(c.config, v.info, warnProveInit, v.name.s)
|
message(c.config, v.info, warnProveInit, v.name.s)
|
||||||
elif tfNotNil in v.typ.flags and not v.astdef.typ.isNil and tfNotNil notin v.astdef.typ.flags:
|
elif tfNotNil in v.typ.flags and not v.astdef.typ.isNil and tfNotNil notin v.astdef.typ.flags:
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||||
if isPure and (let conflict = strTableInclReportConflict(symbols, e); conflict != nil):
|
if isPure and (let conflict = strTableInclReportConflict(symbols, e); conflict != nil):
|
||||||
wrongRedefinition(c, e.info, e.name.s, conflict.info)
|
wrongRedefinition(c, e.info, e.name.s, conflict.info)
|
||||||
inc(counter)
|
inc(counter)
|
||||||
if tfNotNil in e.typ.flags and not hasNull: incl(result.flags, tfNeedsInit)
|
if tfNotNil in e.typ.flags and not hasNull: incl(result.flags, tfRequiresInit)
|
||||||
|
|
||||||
proc semSet(c: PContext, n: PNode, prev: PType): PType =
|
proc semSet(c: PContext, n: PNode, prev: PType): PType =
|
||||||
result = newOrPrevType(tySet, prev, c)
|
result = newOrPrevType(tySet, prev, c)
|
||||||
|
|
@ -254,15 +254,15 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType =
|
||||||
result = semRangeAux(c, n[1], prev)
|
result = semRangeAux(c, n[1], prev)
|
||||||
let n = result.n
|
let n = result.n
|
||||||
if n[0].kind in {nkCharLit..nkUInt64Lit} and n[0].intVal > 0:
|
if n[0].kind in {nkCharLit..nkUInt64Lit} and n[0].intVal > 0:
|
||||||
incl(result.flags, tfNeedsInit)
|
incl(result.flags, tfRequiresInit)
|
||||||
elif n[1].kind in {nkCharLit..nkUInt64Lit} and n[1].intVal < 0:
|
elif n[1].kind in {nkCharLit..nkUInt64Lit} and n[1].intVal < 0:
|
||||||
incl(result.flags, tfNeedsInit)
|
incl(result.flags, tfRequiresInit)
|
||||||
elif n[0].kind in {nkFloatLit..nkFloat64Lit} and
|
elif n[0].kind in {nkFloatLit..nkFloat64Lit} and
|
||||||
n[0].floatVal > 0.0:
|
n[0].floatVal > 0.0:
|
||||||
incl(result.flags, tfNeedsInit)
|
incl(result.flags, tfRequiresInit)
|
||||||
elif n[1].kind in {nkFloatLit..nkFloat64Lit} and
|
elif n[1].kind in {nkFloatLit..nkFloat64Lit} and
|
||||||
n[1].floatVal < 0.0:
|
n[1].floatVal < 0.0:
|
||||||
incl(result.flags, tfNeedsInit)
|
incl(result.flags, tfRequiresInit)
|
||||||
else:
|
else:
|
||||||
if n[1].kind == nkInfix and considerQuotedIdent(c, n[1][0]).s == "..<":
|
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")
|
localError(c.config, n[0].info, "range types need to be constructed with '..', '..<' is not supported")
|
||||||
|
|
|
||||||
|
|
@ -75,16 +75,16 @@ varargso("foo", "bar", "baz")
|
||||||
|
|
||||||
type
|
type
|
||||||
Flago = enum
|
Flago = enum
|
||||||
tfNeedsInit, tfNotNil
|
tfRequiresInit, tfNotNil
|
||||||
|
|
||||||
var s: set[Flago] = {tfNeedsInit}
|
var s: set[Flago] = {tfRequiresInit}
|
||||||
|
|
||||||
if {tfNeedsInit, tfNotNil} * s != {}:
|
if {tfRequiresInit, tfNotNil} * s != {}:
|
||||||
echo "yes"
|
echo "yes"
|
||||||
else:
|
else:
|
||||||
echo "no"
|
echo "no"
|
||||||
|
|
||||||
if {tfNeedsInit, tfNotNil} * s <= {tfNotNil}:
|
if {tfRequiresInit, tfNotNil} * s <= {tfNotNil}:
|
||||||
echo "yes"
|
echo "yes"
|
||||||
else:
|
else:
|
||||||
echo "no"
|
echo "no"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue