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:
ringabout 2022-10-04 18:45:10 +08:00 • committed by GitHub
commit f89ba2c951
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 727 additions and 80 deletions

View file

@ -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