summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorzah <zahary@gmail.com>2017-03-12 10:27:05 +0200
committerAndreas Rumpf <rumpf_a@web.de>2017-03-12 09:27:05 +0100
commit6e358e318747ecd6bea66911d6144cb7eff9d172 (patch)
tree74cb873c5eb65da8394f6d74e9e9c1a1b5148dc0
parentcb9d554ac93480c442a00129eaa0126396c884a3 (diff)
downloadNim-6e358e318747ecd6bea66911d6144cb7eff9d172.tar.gz
don't allow casting to non-concrete types; fixes #5428 (#5502)
-rw-r--r--compiler/msgs.nim2
-rw-r--r--compiler/semexprs.nim13
-rw-r--r--compiler/types.nim1
-rw-r--r--tests/errmsgs/tnon_concrete_cast.nim47
4 files changed, 58 insertions, 5 deletions
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index e50ed0f2a..bf9090089 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -89,6 +89,7 @@ type
     errMainModuleMustBeSpecified,
     errXExpected,
     errTIsNotAConcreteType,
+    errCastToANonConcreteType,
     errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError,
     errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile,
     errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitly,
@@ -326,6 +327,7 @@ const
     errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file",
     errXExpected: "\'$1\' expected",
     errTIsNotAConcreteType: "\'$1\' is not a concrete type.",
+    errCastToANonConcreteType: "cannot cast to a non concrete type: \'$1\'",
     errInvalidSectionStart: "invalid section start",
     errGridTableNotImplemented: "grid table is not implemented",
     errGeneralParseError: "general parse error",
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index a419cd000..755d44448 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -218,13 +218,16 @@ proc semConv(c: PContext, n: PNode): PNode =
 proc semCast(c: PContext, n: PNode): PNode =
   ## Semantically analyze a casting ("cast[type](param)")
   checkSonsLen(n, 2)
+  let targetType = semTypeNode(c, n.sons[0], nil)
+  let castedExpr = semExprWithType(c, n.sons[1])
+  if tfHasMeta in targetType.flags:
+    localError(n.sons[0].info, errCastToANonConcreteType, $targetType)
+  if not isCastable(targetType, castedExpr.typ):
+    localError(n.info, errExprCannotBeCastToX, $targetType)
   result = newNodeI(nkCast, n.info)
-  result.typ = semTypeNode(c, n.sons[0], nil)
+  result.typ = targetType
   addSon(result, copyTree(n.sons[0]))
-  addSon(result, semExprWithType(c, n.sons[1]))
-  if not isCastable(result.typ, result.sons[1].typ):
-    localError(result.info, errExprCannotBeCastToX,
-               typeToString(result.typ))
+  addSon(result, castedExpr)
 
 proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
   const
diff --git a/compiler/types.nim b/compiler/types.nim
index df1d3e3ca..80fb6612d 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -20,6 +20,7 @@ type
     preferName, preferDesc, preferExported, preferModuleInfo, preferGenericArg
 
 proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string
+template `$`*(typ: PType): string = typeToString(typ)
 
 proc base*(t: PType): PType =
   result = t.sons[0]
diff --git a/tests/errmsgs/tnon_concrete_cast.nim b/tests/errmsgs/tnon_concrete_cast.nim
new file mode 100644
index 000000000..e4ae890ce
--- /dev/null
+++ b/tests/errmsgs/tnon_concrete_cast.nim
@@ -0,0 +1,47 @@
+discard """
+  errormsg: "cannot cast to a non concrete type: 'ptr SomeNumber'"
+  line: 36
+"""
+
+# https://github.com/nim-lang/Nim/issues/5428
+
+type
+  MemFile = object
+    mem: pointer
+
+proc memfileopen(filename: string, newFileSize: int): MemFile =
+  # just a memfile mock
+  return
+
+type
+  MyData = object
+    member1: seq[int]
+    member2: int
+
+type
+  MyReadWrite = object
+    memfile: MemFile
+    offset: int
+
+# Here, SomeNumber is bound to a concrete type, and that's OK
+proc write(rw: var MyReadWrite; value: SomeNumber): void =
+  (cast[ptr SomeNumber](cast[uint](rw.memfile.mem) + rw.offset.uint))[] = value
+  rw.offset += sizeof(SomeNumber)
+
+# Here, we try to use SomeNumber without binding it to a type. This should
+# produce an error message for now. It's also possible to relax the rules
+# and allow for type-class based type inference in such situations.
+proc write[T](rw: var MyReadWrite; value: seq[T]): void =
+  rw.write value.len
+  let dst  = cast[ptr SomeNumber](cast[uint](rw.memfile.mem) + uint(rw.offset))
+  let src  = cast[pointer](value[0].unsafeAddr)
+  let size = sizeof(T) * value.len
+  copyMem(dst, src, size)
+  rw.offset += size
+
+proc saveBinFile(arg: var MyData, filename: string): void =
+  var rw: MyReadWrite
+  rw.memfile = memfileOpen(filename, newFileSize = rw.offset)
+  rw.offset = 0
+  rw.write arg.member1
+