mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 08:54:53 +00:00
implement generic default values for object fields (#24384)
fixes #21941, fixes #23594
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 =
|
||||
@@ -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:
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
26
tests/objects/tgenericdefaultvalue.nim
Normal file
26
tests/objects/tgenericdefaultvalue.nim
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user