summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/liftdestructors.nim46
-rw-r--r--compiler/modulegraphs.nim4
-rw-r--r--compiler/sempass2.nim2
-rw-r--r--compiler/sighashes.nim7
-rw-r--r--compiler/sizealignoffsetimpl.nim2
-rw-r--r--lib/pure/dynlib.nim5
-rw-r--r--lib/pure/includes/oserr.nim2
-rw-r--r--lib/pure/osproc.nim8
-rw-r--r--lib/pure/streams.nim14
-rw-r--r--lib/pure/strtabs.nim9
-rw-r--r--lib/pure/times.nim2
-rw-r--r--lib/system.nim2
12 files changed, 57 insertions, 46 deletions
diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim
index 4ae438e48..b85360ead 100644
--- a/compiler/liftdestructors.nim
+++ b/compiler/liftdestructors.nim
@@ -12,6 +12,14 @@
 
 # included from sempass2.nim
 
+# Todo:
+# - specialize destructors for constant arrays of strings
+#   (they don't require any)
+# - use openArray instead of array to avoid over-specializations
+# - make 'owned' mean 'sink' in parameters
+
+import sighashes
+
 type
   TLiftCtx = object
     graph: ModuleGraph
@@ -286,25 +294,10 @@ proc seqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
     doAssert t.destructor != nil
     moveCall.add destructorCall(c.graph, t.destructor, x)
     body.add moveCall
-    when false:
-      # we generate:
-      #  if a.len != 0 and a.p != b.p:
-      #    `=destroy`(x)
-      #  a.len = b.len
-      #  a.p = b.p
-      # Note: '@' is either '.' or '->'.
-      body.add genIf(c, genVerbatim("dest@len != 0 && dest@p != src.p", c.info),
-        destructorCall(c.graph, t.destructor, x))
-      body.add genVerbatim("dest@len=src.len; dest@p=src.p;", c.info)
   of attachedDestructor:
     # destroy all elements:
     forallElements(c, t, body, x, y)
     body.add genBuiltin(c.graph, mDestroy, "destroy", x)
-    when false:
-      var deallocStmt = genVerbatim("dest@region->dealloc(dest@region, dest@p, " &
-          "(dest@p->cap * sizeof($)) + sizeof(NI) + sizeof(void*)); dest@len = 0;", c.info)
-      deallocStmt.typ = t.lastSon
-      body.add genIf(c, genVerbatim("dest@len != 0 && dest@region", c.info), deallocStmt)
 
 proc strOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   case c.kind
@@ -603,19 +596,29 @@ template inst(field, t) =
     if field.ast != nil:
       patchBody(c, field.ast, info)
 
-proc createTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
+proc createTypeBoundOps*(c: PContext; orig: PType; info: TLineInfo) =
   ## In the semantic pass this is called in strategic places
   ## to ensure we lift assignment, destructors and moves properly.
   ## The later 'injectdestructors' pass depends on it.
-  if typ == nil or {tfCheckedForDestructor, tfHasMeta} * typ.flags != {}: return
-  incl typ.flags, tfCheckedForDestructor
+  if orig == nil or {tfCheckedForDestructor, tfHasMeta} * orig.flags != {}: return
+  incl orig.flags, tfCheckedForDestructor
+
+  let h = sighashes.hashType(orig, {CoType, CoConsiderOwned})
+  var canon = c.graph.canonTypes.getOrDefault(h)
+  var overwrite = false
+  if canon == nil:
+    c.graph.canonTypes[h] = orig
+    canon = orig
+  elif canon != orig:
+    overwrite = true
+
   # multiple cases are to distinguish here:
   # 1. we don't know yet if 'typ' has a nontrival destructor.
   # 2. we have a nop destructor. --> mDestroy
   # 3. we have a lifted destructor.
   # 4. We have a custom destructor.
   # 5. We have a (custom) generic destructor.
-  let typ = typ.skipTypes({tyGenericInst, tyAlias})
+  let typ = canon.skipTypes({tyGenericInst, tyAlias})
   # we generate the destructor first so that other operators can depend on it:
   if typ.destructor == nil:
     liftBody(c, typ, attachedDestructor, info)
@@ -629,3 +632,8 @@ proc createTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
     liftBody(c, typ, attachedSink, info)
   else:
     inst(typ.sink, typ)
+
+  if overwrite:
+    orig.destructor = canon.destructor
+    orig.assignment = canon.assignment
+    orig.sink = canon.sink
diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim
index 8c081da08..82b9750b6 100644
--- a/compiler/modulegraphs.nim
+++ b/compiler/modulegraphs.nim
@@ -26,7 +26,7 @@
 ##
 
 import ast, intsets, tables, options, lineinfos, hashes, idents,
-  incremental, btrees
+  incremental, btrees, sighashes
 
 type
   ModuleGraph* = ref object
@@ -57,6 +57,7 @@ type
     opContains*, opNot*: PSym
     emptyNode*: PNode
     incr*: IncrementalCtx
+    canonTypes*: Table[SigHash, PType]
     importModuleCallback*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex): PSym {.nimcall.}
     includeFileCallback*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex): PNode {.nimcall.}
     recordStmt*: proc (graph: ModuleGraph; m: PSym; n: PNode) {.nimcall.}
@@ -138,6 +139,7 @@ proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
   result.cacheSeqs = initTable[string, PNode]()
   result.cacheCounters = initTable[string, BiggestInt]()
   result.cacheTables = initTable[string, BTree[string, PNode]]()
+  result.canonTypes = initTable[SigHash, PType]()
 
 proc resetAllModules*(g: ModuleGraph) =
   initStrTable(g.packageSyms)
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index fa4941ba4..cab79314a 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -10,7 +10,7 @@
 import
   intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
   wordrecg, strutils, options, guards, lineinfos, semfold, semdata,
-  modulegraphs, lowerings, sigmatch
+  modulegraphs, lowerings, sigmatch, tables
 
 when not defined(leanCompiler):
   import writetracking
diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim
index 4247c6caa..19175f3b3 100644
--- a/compiler/sighashes.nim
+++ b/compiler/sighashes.nim
@@ -90,6 +90,7 @@ type
     CoType
     CoOwnerSig
     CoIgnoreRange
+    CoConsiderOwned
 
 proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag])
 
@@ -165,7 +166,11 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
         c.hashType t.sons[i], flags
     else:
       c.hashType t.lastSon, flags
-  of tyAlias, tySink, tyUserTypeClasses, tyInferred, tyOwned:
+  of tyAlias, tySink, tyUserTypeClasses, tyInferred:
+    c.hashType t.lastSon, flags
+  of tyOwned:
+    if CoConsiderOwned in flags:
+      c &= char(t.kind)
     c.hashType t.lastSon, flags
   of tyBool, tyChar, tyInt..tyUInt64:
     # no canonicalization for integral types, so that e.g. ``pid_t`` is
diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim
index 665f4b1ab..45febeea8 100644
--- a/compiler/sizealignoffsetimpl.nim
+++ b/compiler/sizealignoffsetimpl.nim
@@ -413,7 +413,7 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
       typ.size = typ.lastSon.size
       typ.align = typ.lastSon.align
 
-  of tyGenericInst, tyDistinct, tyGenericBody, tyAlias, tySink:
+  of tyGenericInst, tyDistinct, tyGenericBody, tyAlias, tySink, tyOwned:
     computeSizeAlign(conf, typ.lastSon)
     typ.size = typ.lastSon.size
     typ.align = typ.lastSon.align
diff --git a/lib/pure/dynlib.nim b/lib/pure/dynlib.nim
index d030ad532..97bc51bc5 100644
--- a/lib/pure/dynlib.nim
+++ b/lib/pure/dynlib.nim
@@ -72,10 +72,7 @@ proc unloadLib*(lib: LibHandle) {.gcsafe.}
 
 proc raiseInvalidLibrary*(name: cstring) {.noinline, noreturn.} =
   ## raises an `EInvalidLibrary` exception.
-  var e: ref LibraryError
-  new(e)
-  e.msg = "could not find symbol: " & $name
-  raise e
+  raise newException(LibraryError, "could not find symbol: " & $name)
 
 proc symAddr*(lib: LibHandle, name: cstring): pointer {.gcsafe.}
   ## retrieves the address of a procedure/variable from `lib`. Returns nil
diff --git a/lib/pure/includes/oserr.nim b/lib/pure/includes/oserr.nim
index 25e221d3b..947bdd9a9 100644
--- a/lib/pure/includes/oserr.nim
+++ b/lib/pure/includes/oserr.nim
@@ -73,7 +73,7 @@ proc raiseOSError*(errorCode: OSErrorCode; additionalInfo = "") {.noinline.} =
   ## See also:
   ## * `osErrorMsg proc <#osErrorMsg,OSErrorCode>`_
   ## * `osLastError proc <#osLastError>`_
-  var e: ref OSError; new(e)
+  var e: owned(ref OSError); new(e)
   e.errorCode = errorCode.int32
   e.msg = osErrorMsg(errorCode)
   if additionalInfo.len > 0:
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index af0e2e782..1888df813 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -126,7 +126,7 @@ proc startProcess*(command: string,
                    args: openArray[string] = [],
                    env: StringTableRef = nil,
                    options: set[ProcessOption] = {poStdErrToStdOut}):
-              Process {.rtl, extern: "nosp$1", tags: [ExecIOEffect, ReadEnvEffect,
+              owned Process {.rtl, extern: "nosp$1", tags: [ExecIOEffect, ReadEnvEffect,
               RootEffect].}
   ## Starts a process. `Command` is the executable file, `workingDir` is the
   ## process's working directory. If ``workingDir == ""`` the current directory
@@ -470,7 +470,7 @@ when defined(Windows) and not defined(useNimRtl):
                               addr bytesWritten, nil)
     if a == 0: raiseOSError(osLastError())
 
-  proc newFileHandleStream(handle: Handle): FileHandleStream =
+  proc newFileHandleStream(handle: Handle): owned FileHandleStream =
     new(result)
     result.handle = handle
     result.closeImpl = hsClose
@@ -573,7 +573,7 @@ when defined(Windows) and not defined(useNimRtl):
                  workingDir: string = "",
                  args: openArray[string] = [],
                  env: StringTableRef = nil,
-                 options: set[ProcessOption] = {poStdErrToStdOut}): Process =
+                 options: set[ProcessOption] = {poStdErrToStdOut}): owned Process =
     var
       si: STARTUPINFO
       procInfo: PROCESS_INFORMATION
@@ -840,7 +840,7 @@ elif not defined(useNimRtl):
                  workingDir: string = "",
                  args: openArray[string] = [],
                  env: StringTableRef = nil,
-                 options: set[ProcessOption] = {poStdErrToStdOut}): Process =
+                 options: set[ProcessOption] = {poStdErrToStdOut}): owned Process =
     var
       pStdin, pStdout, pStderr: array[0..1, cint]
     new(result)
diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim
index 6c69a9bb6..ff2b72725 100644
--- a/lib/pure/streams.nim
+++ b/lib/pure/streams.nim
@@ -34,7 +34,7 @@
 
 include "system/inclrtl"
 
-proc newEIO(msg: string): ref IOError =
+proc newEIO(msg: string): owned(ref IOError) =
   new(result)
   result.msg = msg
 
@@ -403,7 +403,7 @@ when not defined(js):
     else:
       s.data = nil
 
-  proc newStringStream*(s: string = ""): StringStream =
+  proc newStringStream*(s: string = ""): owned StringStream =
     ## creates a new stream from the string `s`.
     new(result)
     result.data = s
@@ -446,7 +446,7 @@ when not defined(js):
     if writeBuffer(FileStream(s).f, buffer, bufLen) != bufLen:
       raise newEIO("cannot write to stream")
 
-  proc newFileStream*(f: File): FileStream =
+  proc newFileStream*(f: File): owned FileStream =
     ## creates a new stream from the file `f`.
     new(result)
     result.f = f
@@ -460,7 +460,7 @@ when not defined(js):
     result.writeDataImpl = fsWriteData
     result.flushImpl = fsFlush
 
-  proc newFileStream*(filename: string, mode: FileMode = fmRead, bufSize: int = -1): FileStream =
+  proc newFileStream*(filename: string, mode: FileMode = fmRead, bufSize: int = -1): owned FileStream =
     ## creates a new stream from the file named `filename` with the mode `mode`.
     ## If the file cannot be opened, nil is returned. See the `system
     ## <system.html>`_ module for a list of available FileMode enums.
@@ -469,7 +469,7 @@ when not defined(js):
     var f: File
     if open(f, filename, mode, bufSize): result = newFileStream(f)
 
-  proc openFileStream*(filename: string, mode: FileMode = fmRead, bufSize: int = -1): FileStream =
+  proc openFileStream*(filename: string, mode: FileMode = fmRead, bufSize: int = -1): owned FileStream =
     ## creates a new stream from the file named `filename` with the mode `mode`.
     ## If the file cannot be opened, an IO exception is raised.
     var f: File
@@ -522,7 +522,7 @@ else:
         raise newEIO("cannot write to stream")
       inc(s.pos, bufLen)
 
-  proc newFileHandleStream*(handle: FileHandle): FileHandleStream =
+  proc newFileHandleStream*(handle: FileHandle): owned FileHandleStream =
     new(result)
     result.handle = handle
     result.pos = 0
@@ -535,7 +535,7 @@ else:
     result.writeData = hsWriteData
 
   proc newFileHandleStream*(filename: string,
-                            mode: FileMode): FileHandleStream =
+                            mode: FileMode): owned FileHandleStream =
     when defined(windows):
       discard
     else:
diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim
index 2568f83c2..f158bb2fc 100644
--- a/lib/pure/strtabs.nim
+++ b/lib/pure/strtabs.nim
@@ -247,7 +247,7 @@ proc `[]=`*(t: StringTableRef, key, val: string) {.
     rawInsert(t, t.data, key, val)
     inc(t.counter)
 
-proc newStringTable*(mode: StringTableMode): StringTableRef {.
+proc newStringTable*(mode: StringTableMode): owned StringTableRef {.
   rtlFunc, extern: "nst$1".} =
   ## Creates a new empty string table.
   ##
@@ -260,7 +260,7 @@ proc newStringTable*(mode: StringTableMode): StringTableRef {.
   newSeq(result.data, startSize)
 
 proc newStringTable*(keyValuePairs: varargs[string],
-                     mode: StringTableMode): StringTableRef {.
+                     mode: StringTableMode): owned StringTableRef {.
   rtlFunc, extern: "nst$1WithPairs".} =
   ## Creates a new string table with given `key, value` string pairs.
   ##
@@ -290,10 +290,7 @@ proc newStringTable*(keyValuePairs: varargs[tuple[key, val: string]],
   for key, val in items(keyValuePairs): result[key] = val
 
 proc raiseFormatException(s: string) =
-  var e: ref ValueError
-  new(e)
-  e.msg = "format string: key not found: " & s
-  raise e
+  raise newException(ValueError, "format string: key not found: " & s)
 
 proc getValue(t: StringTableRef, flags: set[FormatFlag], key: string): string =
   if hasKey(t, key): return t.getOrDefault(key)
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index 1bb23a311..5fd57c843 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -959,7 +959,7 @@ proc newTimezone*(
           {.tags: [], raises: [], benign.},
       zonedTimeFromAdjTimeImpl: proc (adjTime: Time): ZonedTime
           {.tags: [], raises: [], benign.}
-    ): Timezone =
+    ): owned Timezone =
   ## Create a new ``Timezone``.
   ##
   ## ``zonedTimeFromTimeImpl`` and ``zonedTimeFromAdjTimeImpl`` is used
diff --git a/lib/system.nim b/lib/system.nim
index c0f32961b..7bd3aa625 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -1638,6 +1638,8 @@ when defined(nimV2) and not defined(nimscript):
     new(r)
     return r
 else:
+  template owned*(t: typeDesc): typedesc = t
+
   proc new*[T](a: var ref T) {.magic: "New", noSideEffect.}
     ## Creates a new object of type ``T`` and returns a safe (traced)
     ## reference to it in ``a``.