Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

const now works with ref types #15528

Closed
wants to merge 13 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@
for an object type in the current scope.

- `typeof(voidStmt)` now works and returns `void`.
- `const` now works with types containing `ref`, see `tests/vm/tconstrefs.nim`

- The `gc:orc` algorithm was refined so that custom container types can participate in the
cycle collection process.
Expand Down
4 changes: 3 additions & 1 deletion compiler/ccgexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2724,7 +2724,9 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
internalError(p.config, n.info, "expr: proc not init " & sym.name.s)
putLocIntoDest(p, d, sym.loc)
of skConst:
if isSimpleConst(sym.typ):
if containsTyRef(sym.typ.skipTypes(abstractVar)):
expr(p, sym.ast, d)
elif isSimpleConst(sym.typ):
putIntoDest(p, d, n, genLiteral(p, sym.ast, sym.typ), OnStatic)
elif useAliveDataFromDce in p.module.flags:
genConstHeader(p.module, p.module, p, sym)
Expand Down
4 changes: 2 additions & 2 deletions compiler/isolation_check.nim
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ proc canAlias*(arg, ret: PType): bool =
result = canAlias(arg, ret, marker)

proc checkIsolate*(n: PNode): bool =
if types.containsTyRef(n.typ):
if types.containsTyRefOrClosure(n.typ):
# XXX Maybe require that 'n.typ' is acyclic. This is not much
# worse than the already exisiting inheritance and closure restrictions.
case n.kind
Expand All @@ -95,7 +95,7 @@ proc checkIsolate*(n: PNode): bool =
discard "fine, it is isolated already"
else:
let argType = n[i].typ
if argType != nil and not isCompileTimeOnly(argType) and containsTyRef(argType):
if argType != nil and not isCompileTimeOnly(argType) and containsTyRefOrClosure(argType):
if argType.canAlias(n.typ):
return false
result = true
Expand Down
4 changes: 2 additions & 2 deletions compiler/spawn.nim
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType;

if formals[i].typ.kind in {tyTypeDesc, tyStatic}:
continue
#elif containsTyRef(argType):
#elif containsTyRefOrClosure(argType):
# localError(n[i].info, "'spawn'ed function cannot refer to 'ref'/closure")

let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
Expand Down Expand Up @@ -246,7 +246,7 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType;

let argType = skipTypes(if i < formals.len: formals[i].typ else: n.typ,
abstractInst)
#if containsTyRef(argType):
#if containsTyRefOrClosure(argType):
# localError(n.info, "'spawn'ed function cannot refer to 'ref'/closure")

let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
Expand Down
8 changes: 4 additions & 4 deletions compiler/typeallowed.nim
Original file line number Diff line number Diff line change
Expand Up @@ -161,17 +161,17 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
elif kind in {skVar, skLet}:
result = t[1]
of tyRef:
if kind == skConst: result = t
else: result = typeAllowedAux(marker, t.lastSon, kind, c, flags+{taHeap})
# skConst is now allowed
result = typeAllowedAux(marker, t.lastSon, kind, c, flags+{taHeap})
of tyPtr:
result = typeAllowedAux(marker, t.lastSon, kind, c, flags+{taHeap})
of tySet:
for i in 0..<t.len:
result = typeAllowedAux(marker, t[i], kind, c, flags)
if result != nil: break
of tyObject, tyTuple:
if kind in {skProc, skFunc, skConst} and
t.kind == tyObject and t[0] != nil:
# skConst is now allowed
if kind in {skProc, skFunc} and t.kind == tyObject and t[0] != nil:
result = t
else:
let flags = flags+{taField}
Expand Down
14 changes: 9 additions & 5 deletions compiler/types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ proc searchTypeForAux(t: PType, predicate: TTypePredicate,
if not result: result = searchTypeNodeForAux(t.n, predicate, marker)
of tyGenericInst, tyDistinct, tyAlias, tySink:
result = searchTypeForAux(lastSon(t), predicate, marker)
of tyArray, tySet, tyTuple:
of tyArray, tySet, tyTuple, tySequence:
for i in 0..<t.len:
result = searchTypeForAux(t[i], predicate, marker)
if result: return
Expand Down Expand Up @@ -353,12 +353,16 @@ proc isManagedMemory(t: PType): bool =
proc containsManagedMemory*(typ: PType): bool =
result = searchTypeFor(typ, isManagedMemory)

proc isTyRef(t: PType): bool =
result = t.kind == tyRef or (t.kind == tyProc and t.callConv == ccClosure)
proc containsTyRefOrClosure*(typ: PType): bool =
## returns true if typ contains a ref or closure
proc fn(t: PType): bool =
result = t.kind == tyRef or (t.kind == tyProc and t.callConv == ccClosure)
result = searchTypeFor(typ, fn)

proc containsTyRef*(typ: PType): bool =
# returns true if typ contains a 'ref'
result = searchTypeFor(typ, isTyRef)
proc fn(t: PType): bool =
result = t.kind == tyRef
result = searchTypeFor(typ, fn)

proc isHiddenPointer(t: PType): bool =
result = t.kind in {tyString, tySequence, tyOpenArray, tyVarargs}
Expand Down
9 changes: 2 additions & 7 deletions tests/errmsgs/t5870.nim
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
discard """
errormsg: "invalid type: 'SomeRefObj' in this context: 'seq[SomeRefObj]' for const"
line: 14
"""

# bug #5870
type SomeRefObj = ref object of RootObj
someIntMember: int
Expand All @@ -13,5 +8,5 @@ proc createSomeRefObj(v: int): SomeRefObj=

const compileTimeSeqOfRefObjs = @[createSomeRefObj(100500), createSomeRefObj(2)]

for i in 0..1:
echo compileTimeSeqOfRefObjs[i].someIntMember
doAssert compileTimeSeqOfRefObjs[0].someIntMember == 100500
doAssert compileTimeSeqOfRefObjs[1].someIntMember == 2
116 changes: 116 additions & 0 deletions tests/vm/tconstrefs.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
discard """
targets: "c cpp js"
"""

import std/json

block: # ref objects
type Foo = ref object
x1: int
x2: string
x3: seq[string]
const j1 = Foo(x1: 12, x2: "asdf", x3: @["foo", "bar"])
doAssert j1[] == Foo(x1: 12, x2: "asdf", x3: @["foo", "bar"])[]
doAssert $j1[] == """(x1: 12, x2: "asdf", x3: @["foo", "bar"])"""
doAssert j1.x2 == "asdf"

block: # nested ref objects
type Bar = ref object
b0: int
type Foo2 = ref object
f0: Bar
const f = Foo2(f0: Bar(b0: 1))
doAssert f.f0.b0 == 1

block: # ref object of
type Foo = ref object of RootObj
f0: int
const f = @[Foo(f0: 1), Foo(f0: 2)]
doAssert f[1].f0 == 2
let f2 = f
doAssert f2[1].f0 == 2

type Goo = ref object of Foo
g0: int
const g = @[Goo(g0: 3), Goo(g0: 4, f0: 5)]
doAssert g[0].g0 == 3
doAssert g[0].f0 == 0
doAssert g[1].g0 == 4
doAssert g[1].f0 == 5

block: # complex example
type Bar = ref object
b0: int
type Foo3 = ref object
f0: Bar
f1: array[2, Bar]
f2: seq[Bar]
f3: seq[Foo3]
f4: string

proc initFoo3(s: string): Foo3 =
result = Foo3(f0: Bar(b0: 2))
result.f1 = [nil, Bar(b0: 3)]
result.f2 = @[Bar(b0: 4)]
result.f3 = @[Foo3(f4: s)]
result.f4 = s

const f = initFoo3("abc")
let f2 = f
var f3 = f
var f4 = f.unsafeAddr[]
var f5 = [f,f]

proc fn(a: Foo3) =
# shows we can pass a const ref to a proc
doAssert a.f4 == "abc"

fn(f)

template check(x: Foo3) =
fn(x)
doAssert x.f0.b0 == 2
doAssert x.f1[0] == nil
doAssert x.f1[1].b0 == 3
doAssert x.f2[0].b0 == 4
doAssert x.f3[0].f4 == "abc"
doAssert x.f4 == "abc"

check(f)
check(f2)
check(f3)
check(f4)
check(f5[0])

const f6 = f.f3
doAssert f6[0].f4 == "abc"
let f7 = f6
doAssert f7[0].f4 == "abc"
var f8: array[2,Bar]
f8 = f.f1
doAssert f8[1].b0 == 3
var f9: (Foo3,)
f9[0] = f
doAssert f9[0].f0.b0 == 2

block: # case ref objects
const j = parseJson(""" {"x1":12,"x2":"asdf","x3":[1,2]} """)
const x1 = j["x1"].getInt
const x2 = j["x3"].to(seq[int])
doAssert x1 == 12
doAssert x2 == @[1, 2]
doAssert j["x1"].getInt.static == 12
when false:
# xxx still an issue, related to closed bugs: bug #13081, bug #8015
echo j["x1"].getInt

block: # regression test with closures
type MyProc = proc (x: int): int
proc even(x: int): int = x*3
proc bar(): seq[MyProc] =
result.add even
result.setLen 2 # intentionally leaving 1 unassigned
const a = bar()
when not defined(js): # xxx
doAssert a == bar()
doAssert a[0](2) == 2*3