Skip to content

Commit

Permalink
Nonref object inheritance now hints and converts to base in varargs a…
Browse files Browse the repository at this point in the history
…nd on assignment

array handling and more elaborate tests

Better error message for implict object converstion
  • Loading branch information
beef331 committed Nov 29, 2021
1 parent 20e92d1 commit 40c7309
Show file tree
Hide file tree
Showing 11 changed files with 146 additions and 104 deletions.
11 changes: 7 additions & 4 deletions compiler/ccgexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,9 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
# tfShallow flag for the built-in string type too! So we check only
# here for this flag, where it is reasonably safe to do so
# (for objects, etc.):
if optSeqDestructors in p.config.globalOptions:
if optSeqDestructors in p.config.globalOptions or src.lode.kind in {nkObjUpConv, nkObjDownConv}:
# If it's an up/down conv it's an non-ref -> non-ref inheritance which is a copy or assignment.
# As such the assignment is direct, semantic analysis should handle any errors statically.
linefmt(p, cpsStmts,
"$1 = $2;$n",
[rdLoc(dest), rdLoc(src)])
Expand Down Expand Up @@ -2604,11 +2606,12 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) =
else:
genTypeInfoV1(p.module, dest, n.info)
if nilCheck != nil:
# We only need to do a conversion check if it's a ref object.
# Since with non refs either a copy is done or a ptr to the element is passed,
# there is nothing dynamic with them and the compiler knows the error happens at semantic analysis.
linefmt(p, cpsStmts, "if ($1 && !#isObj($2, $3)){ #raiseObjectConversionError(); $4}$n",
[nilCheck, r, checkFor, raiseInstr(p)])
else:
linefmt(p, cpsStmts, "if (!#isObj($1, $2)){ #raiseObjectConversionError(); $3}$n",
[r, checkFor, raiseInstr(p)])

if n[0].typ.kind != tyObject:
if n.isLValue:
putIntoDest(p, d, n,
Expand Down
2 changes: 2 additions & 0 deletions compiler/lineinfos.nim
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ type
hintUser = "User", hintUserRaw = "UserRaw", hintExtendedContext = "ExtendedContext",
hintMsgOrigin = "MsgOrigin", # since 1.3.5
hintDeclaredLoc = "DeclaredLoc", # since 1.5.1
hintImplicitObjConv = "ImplicitObjConv"

const
MsgKindToStr*: array[TMsgKind, string] = [
Expand Down Expand Up @@ -202,6 +203,7 @@ const
hintExtendedContext: "$1",
hintMsgOrigin: "$1",
hintDeclaredLoc: "$1",
hintImplicitObjConv: "Implicit conversion: Receiver '$2' will not receive fields of sub-type '$1' [$3]"
]

const
Expand Down
7 changes: 6 additions & 1 deletion compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,12 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =

let xx = semExprWithType(c, x, {})
result.add xx
typ = commonType(c, typ, xx.typ)
if {xx.typ.kind, typ.kind} == {tyObject} and typ != xx.typ:
# Check if both are objects before getting common type,
# this prevents `[Derived(), Parent(), Derived()]` from working for non ref objects.
result[i] = typeMismatch(c.config, x.info, typ, xx.typ, x)
else:
typ = commonType(c, typ, xx.typ)
#n[i] = semExprWithType(c, x, {})
#result.add fitNode(c, typ, n[i])
inc(lastIndex)
Expand Down
7 changes: 7 additions & 0 deletions compiler/transf.nim
Original file line number Diff line number Diff line change
Expand Up @@ -569,10 +569,17 @@ proc transformConv(c: PTransf, n: PNode): PNode =
result = transformSons(c, n)
of tyObject:
var diff = inheritanceDiff(dest, source)
template convHint =
if n.kind == nkHiddenSubConv and n.typ.kind notin abstractVarRange:
# Creates hint on converstion to parent type where information is lost,
# presently hints on `proc(thing)` where thing converts to non var base.
rawMessage(c.graph.config, hintImplicitObjConv, [typeToString(source), typeToString(dest), toFileLineCol(c.graph.config, n[1].info)])
if diff < 0:
convHint()
result = newTransNode(nkObjUpConv, n, 1)
result[0] = transform(c, n[1])
elif diff > 0 and diff != high(int):
convHint()
result = newTransNode(nkObjDownConv, n, 1)
result[0] = transform(c, n[1])
else:
Expand Down
12 changes: 0 additions & 12 deletions tests/objects/t4318.nim

This file was deleted.

24 changes: 0 additions & 24 deletions tests/objects/tobj_asgn_dont_slice.nim

This file was deleted.

21 changes: 0 additions & 21 deletions tests/typerel/t4799_1.nim

This file was deleted.

21 changes: 0 additions & 21 deletions tests/typerel/t4799_2.nim

This file was deleted.

21 changes: 0 additions & 21 deletions tests/typerel/t4799_3.nim

This file was deleted.

53 changes: 53 additions & 0 deletions tests/types/tinheritance_arrays.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
discard """
cmd: "nim check --hints:off $file"
action: reject
nimout: '''
tinheritance_arrays.nim(20, 18) Error: type mismatch: got <B> but expected 'A = object'
tinheritance_arrays.nim(20, 23) Error: type mismatch: got <C> but expected 'A = object'
tinheritance_arrays.nim(21, 18) Error: type mismatch: got <B> but expected 'C = object'
tinheritance_arrays.nim(21, 23) Error: type mismatch: got <A> but expected 'C = object'
tinheritance_arrays.nim(22, 23) Error: type mismatch: got <A> but expected 'B = object'
'''
"""


block: # Value test
type
A = object of RootObj
B = object of A
C = object of A

discard [A(), B(), C()]
discard [C(), B(), A()]
discard [B(), B(), A()]
discard [B(), B(), B()]
discard [A(), A(), A()]

block: # ref test
type
A = ref object of RootObj
B = ref object of A
C = ref object of A

discard [A(), B(), C()]
discard [C(), B(), A()]
discard [B(), B(), A()]
discard [B(), B(), B()]
discard [A(), A(), A()]

block: # ptr test
type
A = object of RootObj
B = object of A
C = object of A

template make(t: typedesc): ptr t =
let res = createU(t)
res[] = t()
res

discard [make A, make B, make C]
discard [make C, make B, make A]
discard [make B, make B, make A]
discard [make B, make B, make B]
discard [make A, make A, make A]
71 changes: 71 additions & 0 deletions tests/types/tinheritance_conversion.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
discard """
cmd: "nim c --hint[Conf]:off --verbosity:0 $file"
nimout: '''
Hint: Implicit conversion: Receiver 'Base' will not receive fields of sub-type 'Derived' [tinheritance_conversion.nim(30, 15)] [ImplicitObjConv]
Hint: Implicit conversion: Receiver 'Base' will not receive fields of sub-type 'Derived' [tinheritance_conversion.nim(30, 34)] [ImplicitObjConv]
Hint: Implicit conversion: Receiver 'Base' will not receive fields of sub-type 'Derived2' [tinheritance_conversion.nim(38, 3)] [ImplicitObjConv]
'''
"""


type
Base {.inheritable.} = object
field: int

Derived = object of Base
field2: int
field3: int
Derived2 = object of Base

block: # Value tests
proc test(args: varargs[Base]) =
for x in args:
assert x.field == 0

proc test2(base: var Base) = base.field = 400
proc test3(base: Base) = discard
var a: Derived = Derived(Base())
a = Derived(Base(Derived2()))
test(Derived(), Base(), Derived())
a.field2 = 300
test2(a)
assert a.field == 400
assert a.field2 == 300
var b = Derived2(field: 800)
b.test2()
assert b.field == 400
b.test3()



block: # Ref tests
type
Base = ref object of RootObj
field: int
Derived = ref object of Base
field2: int
Derived2 = ref object of Base

var a: Base = Derived()
assert Derived(a) is Derived
doAssertRaises(ObjectConversionDefect): discard Derived2(a)[]
doAssertRaises(ObjectConversionDefect): discard Base(Derived2()).Derived
assert Base(Derived()) is Base
assert Derived2(Base(Derived2())) is Derived2
assert Derived(Base(Derived())) is Derived

block: # Pointer tests
template make(t: typedesc): ptr t =
let res = createU(t)
res[] = t()
res
var a: ptr Base = make(Derived)
assert (ptr Derived)(a) is (ptr Derived)
doAssertRaises(ObjectConversionDefect): discard (ptr Derived2)(a)[]
doAssertRaises(ObjectConversionDefect):
var a = make(Derived2)
discard (ptr Derived)((ptr Base)(a))
assert (ptr Base)(make(Derived)) is (ptr Base)
assert (ptr Derived2)((ptr Base)(make(Derived2))) is (ptr Derived2)
assert (ptr Derived)((ptr Base)(make(Derived))) is (ptr Derived)

0 comments on commit 40c7309

Please sign in to comment.