Skip to content

Commit 4091576

Browse files
authored
implement generic default values for object fields (#24384)
fixes #21941, fixes #23594
1 parent d618974 commit 4091576

File tree

5 files changed

+65
-23
lines changed

5 files changed

+65
-23
lines changed

compiler/sem.nim

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,7 @@ proc preparePContext*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PCo
733733
result.semInferredLambda = semInferredLambda
734734
result.semGenerateInstance = generateInstance
735735
result.instantiateOnlyProcType = instantiateOnlyProcType
736+
result.fitDefaultNode = fitDefaultNode
736737
result.semTypeNode = semTypeNode
737738
result.instTypeBoundOp = sigmatch.instTypeBoundOp
738739
result.hasUnresolvedArgs = hasUnresolvedArgs

compiler/semdata.nim

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ type
142142
instantiateOnlyProcType*: proc (c: PContext, pt: LayeredIdTable,
143143
prc: PSym, info: TLineInfo): PType
144144
# used by sigmatch for explicit generic instantiations
145+
fitDefaultNode*: proc (c: PContext, n: var PNode, expectedType: PType)
145146
includedFiles*: IntSet # used to detect recursive include files
146147
pureEnumFields*: TStrTable # pure enum fields that can be used unambiguously
147148
userPragmas*: TStrTable

compiler/semtypes.nim

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -273,28 +273,23 @@ proc annotateClosureConv(n: PNode) =
273273
for i in 0..<n.len:
274274
annotateClosureConv(n[i])
275275

276-
proc fitDefaultNode(c: PContext, n: PNode): PType =
276+
proc fitDefaultNode(c: PContext, n: var PNode, expectedType: PType) =
277277
inc c.inStaticContext
278-
let expectedType = if n[^2].kind != nkEmpty: semTypeNode(c, n[^2], nil) else: nil
279-
n[^1] = semConstExpr(c, n[^1], expectedType = expectedType)
280-
let oldType = n[^1].typ
281-
n[^1].flags.incl nfSem
282-
if n[^2].kind != nkEmpty:
283-
if expectedType != nil and oldType != expectedType:
284-
n[^1] = fitNodeConsiderViewType(c, expectedType, n[^1], n[^1].info)
285-
changeType(c, n[^1], expectedType, true) # infer types for default fields value
286-
# bug #22926; be cautious that it uses `semConstExpr` to
287-
# evaulate the default fields; it's only natural to use
288-
# `changeType` to infer types for constant values
289-
# that's also the reason why we don't use `semExpr` to check
290-
# the type since two overlapping error messages might be produced
291-
annotateClosureConv(n)
292-
result = n[^1].typ
293-
else:
294-
result = n[^1].typ
278+
n = semConstExpr(c, n, expectedType = expectedType)
279+
let oldType = n.typ
280+
n.flags.incl nfSem
281+
if expectedType != nil and oldType != expectedType:
282+
n = fitNodeConsiderViewType(c, expectedType, n, n.info)
283+
changeType(c, n, expectedType, true) # infer types for default fields value
284+
# bug #22926; be cautious that it uses `semConstExpr` to
285+
# evaulate the default fields; it's only natural to use
286+
# `changeType` to infer types for constant values
287+
# that's also the reason why we don't use `semExpr` to check
288+
# the type since two overlapping error messages might be produced
289+
annotateClosureConv(n)
295290
# xxx any troubles related to defaults fields, consult `semConst` for a potential answer
296-
if n[^1].kind != nkNilLit:
297-
typeAllowedCheck(c, n.info, result, skConst, {taProcContextIsNotMacro, taIsDefaultField})
291+
if n.kind != nkNilLit:
292+
typeAllowedCheck(c, n.info, n.typ, skConst, {taProcContextIsNotMacro, taIsDefaultField})
298293
dec c.inStaticContext
299294

300295
proc isRecursiveType*(t: PType): bool =
@@ -521,7 +516,14 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
521516
checkMinSonsLen(a, 3, c.config)
522517
var hasDefaultField = a[^1].kind != nkEmpty
523518
if hasDefaultField:
524-
typ = fitDefaultNode(c, a)
519+
typ = if a[^2].kind != nkEmpty: semTypeNode(c, a[^2], nil) else: nil
520+
if c.inGenericContext > 0:
521+
a[^1] = semExprWithType(c, a[^1], {efDetermineType, efAllowSymChoice}, typ)
522+
if typ == nil:
523+
typ = a[^1].typ
524+
else:
525+
fitDefaultNode(c, a[^1], typ)
526+
typ = a[^1].typ
525527
elif a[^2].kind != nkEmpty:
526528
typ = semTypeNode(c, a[^2], nil)
527529
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,
885887
var typ: PType
886888
var hasDefaultField = n[^1].kind != nkEmpty
887889
if hasDefaultField:
888-
typ = fitDefaultNode(c, n)
889-
propagateToOwner(rectype, typ)
890+
typ = if n[^2].kind != nkEmpty: semTypeNode(c, n[^2], nil) else: nil
891+
if c.inGenericContext > 0:
892+
n[^1] = semExprWithType(c, n[^1], {efDetermineType, efAllowSymChoice}, typ)
893+
if typ == nil:
894+
typ = n[^1].typ
895+
else:
896+
fitDefaultNode(c, n[^1], typ)
897+
typ = n[^1].typ
898+
propagateToOwner(rectype, typ)
890899
elif n[^2].kind == nkEmpty:
891900
localError(c.config, n.info, errTypeExpected)
892901
typ = errorType(c)

compiler/semtypinst.nim

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,11 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PT
276276
replaceTypeVarsS(cl, n.sym, result.typ)
277277
else:
278278
replaceTypeVarsS(cl, n.sym, replaceTypeVarsT(cl, n.sym.typ))
279+
if result.sym.kind == skField and result.sym.ast != nil and
280+
(cl.owner == nil or result.sym.owner == cl.owner):
281+
# instantiate default value of object/tuple field
282+
cl.c.fitDefaultNode(cl.c, result.sym.ast, result.sym.typ)
283+
result.sym.typ = result.sym.ast.typ
279284
# sym type can be nil if was gensym created by macro, see #24048
280285
if result.sym.typ != nil and result.sym.typ.kind == tyVoid:
281286
# don't add the 'void' field
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
block: # issue #23594
2+
type
3+
Gen[T] = object
4+
a: T = 1.0
5+
6+
Spec32 = Gen[float32]
7+
Spec64 = Gen[float64]
8+
9+
var
10+
a: Spec32
11+
b: Spec64
12+
doAssert sizeof(a) == 4
13+
doAssert sizeof(b) == 8
14+
doAssert a.a is float32
15+
doAssert b.a is float64
16+
17+
block: # issue #21941
18+
func what[T](): T =
19+
123
20+
21+
type MyObject[T] = object
22+
f: T = what[T]()
23+
24+
var m: MyObject[float] = MyObject[float]()
25+
doAssert m.f is float
26+
doAssert m.f == 123.0

0 commit comments

Comments
 (0)