Skip to content

Commit

Permalink
implement generic default values for object fields (#24384)
Browse files Browse the repository at this point in the history
fixes #21941, fixes #23594
  • Loading branch information
metagn authored Oct 30, 2024
1 parent d618974 commit 4091576
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 23 deletions.
1 change: 1 addition & 0 deletions compiler/sem.nim
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,7 @@ proc preparePContext*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PCo
result.semInferredLambda = semInferredLambda
result.semGenerateInstance = generateInstance
result.instantiateOnlyProcType = instantiateOnlyProcType
result.fitDefaultNode = fitDefaultNode
result.semTypeNode = semTypeNode
result.instTypeBoundOp = sigmatch.instTypeBoundOp
result.hasUnresolvedArgs = hasUnresolvedArgs
Expand Down
1 change: 1 addition & 0 deletions compiler/semdata.nim
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ type
instantiateOnlyProcType*: proc (c: PContext, pt: LayeredIdTable,
prc: PSym, info: TLineInfo): PType
# used by sigmatch for explicit generic instantiations
fitDefaultNode*: proc (c: PContext, n: var PNode, expectedType: PType)
includedFiles*: IntSet # used to detect recursive include files
pureEnumFields*: TStrTable # pure enum fields that can be used unambiguously
userPragmas*: TStrTable
Expand Down
55 changes: 32 additions & 23 deletions compiler/semtypes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -273,28 +273,23 @@ proc annotateClosureConv(n: PNode) =
for i in 0..<n.len:
annotateClosureConv(n[i])

proc fitDefaultNode(c: PContext, n: PNode): PType =
proc fitDefaultNode(c: PContext, n: var PNode, expectedType: PType) =
inc c.inStaticContext
let expectedType = if n[^2].kind != nkEmpty: semTypeNode(c, n[^2], nil) else: nil
n[^1] = semConstExpr(c, n[^1], expectedType = expectedType)
let oldType = n[^1].typ
n[^1].flags.incl nfSem
if n[^2].kind != nkEmpty:
if expectedType != nil and oldType != expectedType:
n[^1] = fitNodeConsiderViewType(c, expectedType, n[^1], n[^1].info)
changeType(c, n[^1], expectedType, true) # infer types for default fields value
# bug #22926; be cautious that it uses `semConstExpr` to
# evaulate the default fields; it's only natural to use
# `changeType` to infer types for constant values
# that's also the reason why we don't use `semExpr` to check
# the type since two overlapping error messages might be produced
annotateClosureConv(n)
result = n[^1].typ
else:
result = n[^1].typ
n = semConstExpr(c, n, expectedType = expectedType)
let oldType = n.typ
n.flags.incl nfSem
if expectedType != nil and oldType != expectedType:
n = fitNodeConsiderViewType(c, expectedType, n, n.info)
changeType(c, n, expectedType, true) # infer types for default fields value
# bug #22926; be cautious that it uses `semConstExpr` to
# evaulate the default fields; it's only natural to use
# `changeType` to infer types for constant values
# that's also the reason why we don't use `semExpr` to check
# the type since two overlapping error messages might be produced
annotateClosureConv(n)
# xxx any troubles related to defaults fields, consult `semConst` for a potential answer
if n[^1].kind != nkNilLit:
typeAllowedCheck(c, n.info, result, skConst, {taProcContextIsNotMacro, taIsDefaultField})
if n.kind != nkNilLit:
typeAllowedCheck(c, n.info, n.typ, skConst, {taProcContextIsNotMacro, taIsDefaultField})
dec c.inStaticContext

proc isRecursiveType*(t: PType): bool =
Expand Down Expand Up @@ -521,7 +516,14 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
checkMinSonsLen(a, 3, c.config)
var hasDefaultField = a[^1].kind != nkEmpty
if hasDefaultField:
typ = fitDefaultNode(c, a)
typ = if a[^2].kind != nkEmpty: semTypeNode(c, a[^2], nil) else: nil
if c.inGenericContext > 0:
a[^1] = semExprWithType(c, a[^1], {efDetermineType, efAllowSymChoice}, typ)
if typ == nil:
typ = a[^1].typ
else:
fitDefaultNode(c, a[^1], typ)
typ = a[^1].typ
elif a[^2].kind != nkEmpty:
typ = semTypeNode(c, a[^2], nil)
if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange:
Expand Down Expand Up @@ -885,8 +887,15 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
var typ: PType
var hasDefaultField = n[^1].kind != nkEmpty
if hasDefaultField:
typ = fitDefaultNode(c, n)
propagateToOwner(rectype, typ)
typ = if n[^2].kind != nkEmpty: semTypeNode(c, n[^2], nil) else: nil
if c.inGenericContext > 0:
n[^1] = semExprWithType(c, n[^1], {efDetermineType, efAllowSymChoice}, typ)
if typ == nil:
typ = n[^1].typ
else:
fitDefaultNode(c, n[^1], typ)
typ = n[^1].typ
propagateToOwner(rectype, typ)
elif n[^2].kind == nkEmpty:
localError(c.config, n.info, errTypeExpected)
typ = errorType(c)
Expand Down
5 changes: 5 additions & 0 deletions compiler/semtypinst.nim
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,11 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PT
replaceTypeVarsS(cl, n.sym, result.typ)
else:
replaceTypeVarsS(cl, n.sym, replaceTypeVarsT(cl, n.sym.typ))
if result.sym.kind == skField and result.sym.ast != nil and
(cl.owner == nil or result.sym.owner == cl.owner):
# instantiate default value of object/tuple field
cl.c.fitDefaultNode(cl.c, result.sym.ast, result.sym.typ)
result.sym.typ = result.sym.ast.typ
# sym type can be nil if was gensym created by macro, see #24048
if result.sym.typ != nil and result.sym.typ.kind == tyVoid:
# don't add the 'void' field
Expand Down
26 changes: 26 additions & 0 deletions tests/objects/tgenericdefaultvalue.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
block: # issue #23594
type
Gen[T] = object
a: T = 1.0

Spec32 = Gen[float32]
Spec64 = Gen[float64]

var
a: Spec32
b: Spec64
doAssert sizeof(a) == 4
doAssert sizeof(b) == 8
doAssert a.a is float32
doAssert b.a is float64

block: # issue #21941
func what[T](): T =
123

type MyObject[T] = object
f: T = what[T]()

var m: MyObject[float] = MyObject[float]()
doAssert m.f is float
doAssert m.f == 123.0

0 comments on commit 4091576

Please sign in to comment.