Skip to content

Commit

Permalink
Merge #74
Browse files Browse the repository at this point in the history
74: Nonref object inheritance hints and converts to base in varargs a… r=saem a=beef331

…nd on assignment

This should make it so non ref objects can be used as one expected. `varargs[Base]` can accept `[Base, Derived, Base, Derived2]` and so forth. It also makes it so any object conversion up or down(Though down conversion is explicit so no clue why it checks) emits a hint to inform about loss of possible information. `var a: Base = Derived(field2: 300)` emits `'Derived(field2: 300)' implicitly converted to 'Base' [/home/jason/nimskull/bin/abuse.nim(26, 22)] [ImplicitObjConv]`

Co-authored-by: Jason <[email protected]>
  • Loading branch information
bors[bot] and beef331 authored Nov 29, 2021
2 parents 20e92d1 + 40c7309 commit 05b2050
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 05b2050

Please sign in to comment.