Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

progess default field #19755

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions compiler/ast.nim
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,7 @@ type
nfLastRead # this node is a last read
nfFirstWrite# this node is a first write
nfHasComment # node has a comment
nfUseDefaultField
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a better way, query sym.ast and see if it contains a value and not nkEmpty.

Copy link
Member Author

@ringabout ringabout Jul 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This flag is to solve field visible problem.

  Default* = object
    poi: int = 12
    clc: Clean
    se*: range[0'i32 .. high(int32)]

poi is not a public object attribute. For a object construct in other modules, I need to distinguish default construct from manual construct.

Default() should pass while Default(poi: 15) should raise. And The ast of Default isn't empty.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand your remark. What does it mean "it should raise"?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The flag is used to indicate that this field has been used as the default object construction so that the semConstr won't issues an error for private fields.

For example, I have a a.nim module which has a Default object with private fields.

# a.nim
type
  Default* = object
    id: int = 1

Now I imports it in the b.nim module. var x: Default will be transformed into var x: Default = Default(id: 1). id is a private field, we need to use .ast != nil or
a new flag to let the compiler allow this.

# b.nim
import a

var x: Default

However .ast != nil doesn't work in the case that users use the id field wrongly. Even if id is a private field, users can still use it in the other modules, which causes confusion.

# b.nim
import a

var x = Default(id: 10)

Using .ast != nil cannot distinguish the two cases.


TNodeFlags* = set[TNodeFlag]
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 45)
Expand Down Expand Up @@ -1432,6 +1433,17 @@ proc newIntTypeNode*(intVal: BiggestInt, typ: PType): PNode =
result.intVal = intVal
result.typ = typ

proc newFloatTypeNode*(floatVal: BiggestFloat, typ: PType): PNode =
let kind = skipTypes(typ, abstractVarRange).kind
case kind
of tyFloat32:
result = newNode(nkFloat32Lit)
of tyFloat64, tyFloat:
result = newNode(nkFloatLit)
else: doAssert false, $kind
result.floatVal = floatVal
result.typ = typ

proc newIntTypeNode*(intVal: Int128, typ: PType): PNode =
# XXX: introduce range check
newIntTypeNode(castToInt64(intVal), typ)
Expand Down
6 changes: 1 addition & 5 deletions compiler/parser.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1963,11 +1963,7 @@ proc parseObjectCase(p: var Parser): PNode =
#| | 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)
Expand Down
158 changes: 158 additions & 0 deletions compiler/sem.nim
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,164 @@ 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, hasDefault: var bool): seq[PNode] =
case recNode.kind
of nkRecList:
for field in recNode:
result.add defaultFieldsForTheUninitialized(c, field, hasDefault)
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], hasDefault)
of nkSym:
let field = recNode.sym
let recType = recNode.typ.skipTypes({tyGenericInst, tyAlias, tySink})
if field.ast != nil: #Try to use default value
result.add newTree(nkExprColonExpr, recNode, field.ast)
hasDefault = true
elif recType.kind == tyObject:
var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, recNode.info, recNode.typ))
asgnExpr.typ = recNode.typ
asgnExpr.flags.incl nfUseDefaultField
asgnExpr.sons.add defaultFieldsForTheUninitialized(c, recType.n, hasDefault)
result.add newTree(nkExprColonExpr, recNode, asgnExpr)
elif recType.kind == tyArray and recType[1].skipTypes({tyGenericInst, tyAlias, tySink}).kind == tyObject:
var objExpr = newTree(nkObjConstr, newNodeIT(nkType, recNode.info, recType[1]))
objExpr.typ = recType[1].skipTypes({tyGenericInst, tyAlias, tySink})
var t = objExpr.typ
while true:
objExpr.sons.add defaultFieldsForTheUninitialized(c, t.n, hasDefault)
let base = t[0]
if base == nil:
break
t = skipTypes(base, skipPtrs)


let node = newNode(nkIntLit)
node.intVal = toInt(lengthOrd(c.graph.config, recType))
var asgnExpr = newTree(nkCall, newSymNode(getSysSym(c.graph, recNode.info, "newDefaultArray"), recNode.info),
# var asgnExpr = newTree(nkCall, newSymNode(getCompilerProc(c.graph, "newDefaultArray")),
node,
objExpr
)
asgnExpr.typ = recNode.typ
asgnExpr.flags.incl nfUseDefaultField

# asgnExpr.sons.setLen(toInt(lengthOrd(c, recType)))
# for i in 0..<asgnExpr.sons.len:
# asgnExpr[i] = objExpr
let asgnExpr2 = semExprWithType(c, asgnExpr)
result.add newTree(nkExprColonExpr, recNode, asgnExpr2)

# asgnExpr.sons.setLen(toInt(lengthOrd(c.graph.config, recType)))
# for i in 0..<asgnExpr.sons.len:
# asgnExpr[i] = objExpr
# result.add newTree(nkExprColonExpr, recNode,
# asgnExpr)
elif recType.kind == tyTuple:
# doAssert false
discard
elif recType.kind in {tyInt..tyInt64, tyUInt..tyUInt64}:
let asgnExpr = newIntTypeNode(int64(0), recType)
asgnExpr.flags.incl nfUseDefaultField
result.add newTree(nkExprColonExpr, recNode,
asgnExpr)
elif recType.kind in tyFloat..tyFloat64:
let asgnExpr = newFloatTypeNode(BiggestFloat(0.0), recType)
asgnExpr.flags.incl nfUseDefaultField
result.add newTree(nkExprColonExpr, recNode,
asgnExpr)

else:
assert false

proc defaultNodeField(c: PContext, a: PNode): PNode =
let aTyp = a.typ
var hasDefault: bool
if aTyp.skipTypes({tyGenericInst, tyAlias, tySink}).kind == tyObject:
var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, a.info, aTyp))
asgnExpr.typ = aTyp
asgnExpr.sons.add defaultFieldsForTheUninitialized(c, aTyp.skipTypes({tyGenericInst, tyAlias, tySink}).n, hasDefault)
if hasDefault:
result = asgnExpr
elif aTyp.kind == tyArray and aTyp[1].skipTypes({tyGenericInst, tyAlias, tySink}).kind == tyObject:
var objExpr = newTree(nkObjConstr, newNodeIT(nkType, a.info, aTyp[1]))
objExpr.typ = aTyp[1].skipTypes({tyGenericInst, tyAlias, tySink})
# objExpr.sons.add defaults # todo create clean?

var t = objExpr.typ
while true:
objExpr.sons.add defaultFieldsForTheUninitialized(c, t.n, hasDefault)
let base = t[0]
if base == nil:
break
t = skipTypes(base, skipPtrs)

let node = newNode(nkIntLit)
node.intVal = toInt(lengthOrd(c.graph.config, aTyp))
var asgnExpr = newTree(nkCall, newSymNode(getSysSym(c.graph, a.info, "newDefaultArray"), a.info),
# var asgnExpr = newTree(nkCall, newSymNode(getCompilerProc(c.graph, "newDefaultArray")),
node,
objExpr
)
asgnExpr.typ = a.typ
asgnExpr.flags.incl nfUseDefaultField

# asgnExpr.sons.setLen(toInt(lengthOrd(c, recType)))
# for i in 0..<asgnExpr.sons.len:
# asgnExpr[i] = objExpr
let asgnExpr2 = semExprWithType(c, asgnExpr)
if hasDefault:
result = asgnExpr2
elif aTyp.kind == tyTuple:
var tupleExpr = newNodeI(nkTupleConstr, a.info)
tupleExpr.typ = aTyp
for s in aTyp.sons:
let sType = s.skipTypes({tyGenericInst, tyAlias, tySink})
if sType.kind == tyObject:
var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, a.info, s))
asgnExpr.typ = s
asgnExpr.sons.add defaultFieldsForTheUninitialized(c, sType.n, hasDefault)
tupleExpr.add asgnExpr
elif sType.kind in {tyInt..tyInt64, tyUInt..tyUInt64}:
let asgnExpr = newIntTypeNode(int64(0), sType)
asgnExpr.flags.incl nfUseDefaultField
tupleExpr.add asgnExpr
elif sType.kind in tyFloat..tyFloat64:
let asgnExpr =
if sType.kind == tyFloat32:
newFloatNode(nkFloat32Lit, BiggestFloat(0.0))
else:
newFloatNode(nkFloatLit, BiggestFloat(0.0))
asgnExpr.flags.incl nfUseDefaultField
tupleExpr.add asgnExpr
if hasDefault:
result = tupleExpr

include semtempl, semgnrc, semstmts, semexprs

proc addCodeForGenerics(c: PContext, n: PNode) =
Expand Down
12 changes: 10 additions & 2 deletions compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -898,8 +898,6 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
rawAddSon(typ, result.typ)
result.typ = typ

proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode

proc resolveIndirectCall(c: PContext; n, nOrig: PNode;
t: PType): TCandidate =
initCandidate(c, result, t)
Expand Down Expand Up @@ -2383,6 +2381,16 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
of mSizeOf:
markUsed(c, n.info, s)
result = semSizeof(c, setMs(n, s))
of mDefault:
result = semDirectOp(c, n, flags)
let typ = result[^1].typ.skipTypes({tyTypeDesc})
if typ.skipTypes({tyGenericInst, tyAlias, tySink}).kind == tyObject:
var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, result[^1].info, typ))
asgnExpr.typ = typ
var hasDefault: bool
asgnExpr.sons.add defaultFieldsForTheUninitialized(c, typ.skipTypes({tyGenericInst, tyAlias, tySink}).n, hasDefault)
if hasDefault:
result = semObjConstr(c, asgnExpr, flags)
else:
result = semDirectOp(c, n, flags)

Expand Down
19 changes: 19 additions & 0 deletions compiler/semmagic.nim
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
# This include file implements the semantic checking for magics.
# included from sem.nim

proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode

proc semAddrArg(c: PContext; n: PNode): PNode =
let x = semExprWithType(c, n)
if x.kind == nkSym:
Expand Down Expand Up @@ -512,6 +514,23 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
result = n
else:
result = plugin(c, n)
of mNew:
result = n
let typ = result[^1].typ
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 hasDefault: bool
var t = typ.skipTypes({tyGenericInst, tyAlias, tySink})[0]
while true:
asgnExpr.sons.add defaultFieldsForTheUninitialized(c, t.n, hasDefault)
let base = t[0]
if base == nil:
break
t = skipTypes(base, skipPtrs)

if hasDefault: # todo apply default
result = newTree(nkAsgn, result[^1], asgnExpr)
of mNewFinalize:
# Make sure the finalizer procedure refers to a procedure
if n[^1].kind == nkSym and n[^1].sym.kind notin {skProc, skFunc}:
Expand Down
Loading