add default field support for object in ARC/ORC (#20480)
* fresh start * add cpp target * add result support * add nimPreviewRangeDefault * reduce * use orc * refactor common parts * add tuple support * add testcase for tuple * cleanup; fixes nimsuggest tests * there is something wrong with cpp * remove * add support for seqs * fixes style * addd initial distinct support * remove links * typo * fixes tuple defaults * add rangedefault * add cpp support * fixes one more bugs * add more hasDefaults * fixes ordinal types * add testcase for #16744 * add testcase for #3608 * fixes docgen * small fix * recursive * fixes * cleanup and remove tuple support * fixes nimsuggest * fixes generics procs * refactor * increases timeout * refactor hasDefault * zero default; disable i386 * add tuples back * fixes bugs * fixes tuple * add more tests * fix one more bug regarding tuples * more tests and cleanup * remove messy distinct types which must be initialized by original types * add tests * fixes zero default * fixes grammar * fixes tests * fixes tests * fixes tests * fixes comments * fixes and add testcase * undo default values for results Co-authored-by: flywind <43030857+xflywind@users.noreply.github.com>
This commit is contained in:
parent
6cf0727192
commit
f89ba2c951
23 changed files with 727 additions and 80 deletions
|
|
@ -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..<branch.len-1:
|
||||
if branch[i].kind == nkRange:
|
||||
if overlap(branch[i], matched): return true
|
||||
elif exprStructuralEquivalent(branch[i], matched):
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
proc branchVals(c: PContext, caseNode: PNode, caseIdx: int,
|
||||
isStmtBranch: bool): IntSet =
|
||||
if caseNode[caseIdx].kind == nkOfBranch:
|
||||
|
|
@ -155,18 +152,14 @@ proc collectMissingFields(c: PContext, fieldsRecList: PNode,
|
|||
if assignment == nil:
|
||||
constrCtx.missingFields.add r.sym
|
||||
|
||||
|
||||
proc semConstructFields(c: PContext, n: PNode,
|
||||
constrCtx: var ObjConstrContext,
|
||||
flags: TExprFlags): InitStatus =
|
||||
result = initUnknown
|
||||
|
||||
proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
|
||||
flags: TExprFlags): tuple[status: InitStatus, defaults: seq[PNode]] =
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
for field in n:
|
||||
let status = semConstructFields(c, field, constrCtx, flags)
|
||||
mergeInitStatus(result, status)
|
||||
|
||||
let (subSt, subDf) = semConstructFields(c, field, constrCtx, flags)
|
||||
result.status.mergeInitStatus subSt
|
||||
result.defaults.add subDf
|
||||
of nkRecCase:
|
||||
template fieldsPresentInBranch(branchIdx: int): string =
|
||||
let branch = n[branchIdx]
|
||||
|
|
@ -184,9 +177,9 @@ proc semConstructFields(c: PContext, n: PNode,
|
|||
|
||||
for i in 1..<n.len:
|
||||
let innerRecords = n[i][^1]
|
||||
let status = semConstructFields(c, innerRecords, constrCtx, flags)
|
||||
let (status, _) = semConstructFields(c, innerRecords, constrCtx, flags) # todo
|
||||
if status notin {initNone, initUnknown}:
|
||||
mergeInitStatus(result, status)
|
||||
result.status.mergeInitStatus status
|
||||
if selectedBranch != -1:
|
||||
let prevFields = fieldsPresentInBranch(selectedBranch)
|
||||
let currentFields = fieldsPresentInBranch(i)
|
||||
|
|
@ -194,7 +187,7 @@ proc semConstructFields(c: PContext, n: PNode,
|
|||
("The fields '$1' and '$2' cannot be initialized together, " &
|
||||
"because they are from conflicting branches in the case object.") %
|
||||
[prevFields, currentFields])
|
||||
result = initConflict
|
||||
result.status = initConflict
|
||||
else:
|
||||
selectedBranch = i
|
||||
|
||||
|
|
@ -206,7 +199,7 @@ proc semConstructFields(c: PContext, n: PNode,
|
|||
("cannot prove that it's safe to initialize $1 with " &
|
||||
"the runtime value for the discriminator '$2' ") %
|
||||
[fields, discriminator.sym.name.s])
|
||||
mergeInitStatus(result, initNone)
|
||||
mergeInitStatus(result.status, initNone)
|
||||
|
||||
template wrongBranchError(i) =
|
||||
if c.inUncheckedAssignSection == 0:
|
||||
|
|
@ -289,13 +282,16 @@ proc semConstructFields(c: PContext, n: PNode,
|
|||
else:
|
||||
wrongBranchError(failedBranch)
|
||||
|
||||
let (_, defaults) = semConstructFields(c, branchNode[^1], constrCtx, flags)
|
||||
result.defaults.add defaults
|
||||
|
||||
# When a branch is selected with a partial match, some of the fields
|
||||
# that were not initialized may be mandatory. We must check for this:
|
||||
if result == initPartial:
|
||||
if result.status == initPartial:
|
||||
collectMissingFields branchNode
|
||||
|
||||
else:
|
||||
result = initNone
|
||||
result.status = initNone
|
||||
let discriminatorVal = semConstrField(c, flags + {efPreferStatic},
|
||||
discriminator.sym,
|
||||
constrCtx.initExpr)
|
||||
|
|
@ -308,33 +304,41 @@ proc semConstructFields(c: PContext, n: PNode,
|
|||
let matchedBranch = n.pickCaseBranch defaultValue
|
||||
collectMissingFields matchedBranch
|
||||
else:
|
||||
result = initPartial
|
||||
result.status = initPartial
|
||||
if discriminatorVal.kind == nkIntLit:
|
||||
# When the discriminator is a compile-time value, we also know
|
||||
# which branch will be selected:
|
||||
let matchedBranch = n.pickCaseBranch discriminatorVal
|
||||
if matchedBranch != nil: collectMissingFields matchedBranch
|
||||
if matchedBranch != nil:
|
||||
let (_, defaults) = semConstructFields(c, matchedBranch[^1], constrCtx, flags)
|
||||
result.defaults.add defaults
|
||||
collectMissingFields matchedBranch
|
||||
else:
|
||||
# All bets are off. If any of the branches has a mandatory
|
||||
# fields we must produce an error:
|
||||
for i in 1..<n.len: collectMissingFields n[i]
|
||||
|
||||
of nkSym:
|
||||
let field = n.sym
|
||||
let e = semConstrField(c, flags, field, constrCtx.initExpr)
|
||||
result = if e != nil: initFull else: initNone
|
||||
|
||||
if e != nil:
|
||||
result.status = initFull
|
||||
elif field.ast != nil:
|
||||
result.status = initUnknown
|
||||
result.defaults.add newTree(nkExprColonExpr, n, field.ast)
|
||||
else:
|
||||
result.status = initNone
|
||||
else:
|
||||
internalAssert c.config, false
|
||||
|
||||
proc semConstructTypeAux(c: PContext,
|
||||
constrCtx: var ObjConstrContext,
|
||||
flags: TExprFlags): InitStatus =
|
||||
result = initUnknown
|
||||
flags: TExprFlags): tuple[status: InitStatus, defaults: seq[PNode]] =
|
||||
result.status = initUnknown
|
||||
var t = constrCtx.typ
|
||||
while true:
|
||||
let status = semConstructFields(c, t.n, constrCtx, flags)
|
||||
mergeInitStatus(result, status)
|
||||
let (status, defaults) = semConstructFields(c, t.n, constrCtx, flags)
|
||||
result.status.mergeInitStatus status
|
||||
result.defaults.add defaults
|
||||
if status in {initPartial, initNone, initUnknown}:
|
||||
collectMissingFields c, t.n, constrCtx
|
||||
let base = t[0]
|
||||
|
|
@ -378,7 +382,9 @@ proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) =
|
|||
proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
|
||||
var t = semTypeNode(c, n[0], nil)
|
||||
result = newNodeIT(nkObjConstr, n.info, t)
|
||||
for child in n: result.add child
|
||||
result.add newNodeIT(nkType, n.info, t) #This will contain the default values to be added in transf
|
||||
for i in 1..<n.len:
|
||||
result.add n[i]
|
||||
|
||||
if t == nil:
|
||||
return localErrorNode(c, result, "object constructor needs an object type")
|
||||
|
|
@ -409,7 +415,8 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
|
|||
# field (if this is a case object, initialized fields in two different
|
||||
# branches will be reported as an error):
|
||||
var constrCtx = initConstrContext(t, result)
|
||||
let initResult = semConstructTypeAux(c, constrCtx, flags)
|
||||
let (initResult, defaults) = semConstructTypeAux(c, constrCtx, flags)
|
||||
result[0].sons.add defaults
|
||||
var hasError = false # needed to split error detect/report for better msgs
|
||||
|
||||
# It's possible that the object was not fully initialized while
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue