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

Nonref object inheritance hints and converts to base in varargs a… #74

Merged
merged 1 commit into from
Nov 29, 2021
Merged
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
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 """
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@beef331 is the intention with these tests to fold into the spec?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It probably should be moved to the spec I guess since it's for regression assurance.

Copy link
Collaborator

@saem saem Nov 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, if you know how to do it now then go for it, otherwise ask @haxscramper when he's about and do it later?

(we can merge this in the meantime)

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)