summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/semexprs.nim35
-rw-r--r--compiler/seminst.nim9
-rw-r--r--compiler/sigmatch.nim6
-rw-r--r--tests/errmsgs/t5167_5.nim4
-rw-r--r--tests/generics/t23186.nim155
-rw-r--r--tests/generics/t6137.nim4
-rw-r--r--tests/generics/timplicit_and_explicit.nim3
-rw-r--r--tests/misc/t8545.nim3
8 files changed, 196 insertions, 23 deletions
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 009458089..70206c6f9 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -54,17 +54,6 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   # same as 'semExprWithType' but doesn't check for proc vars
   result = semExpr(c, n, flags + {efOperand, efAllowSymChoice})
   if result.typ != nil:
-    # XXX tyGenericInst here?
-    if result.typ.kind == tyProc and hasUnresolvedParams(result, {efOperand}):
-      #and tfUnresolved in result.typ.flags:
-      let owner = result.typ.owner
-      let err =
-        # consistent error message with evaltempl/semMacroExpr
-        if owner != nil and owner.kind in {skTemplate, skMacro}:
-          errMissingGenericParamsForTemplate % n.renderTree
-        else:
-          errProcHasNoConcreteType % n.renderTree
-      localError(c.config, n.info, err)
     if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
   elif {efWantStmt, efAllowStmt} * flags != {}:
     result.typ = newTypeS(tyVoid, c)
@@ -1013,6 +1002,30 @@ proc bracketedMacro(n: PNode): PSym =
   else:
     result = nil
 
+proc finishOperand(c: PContext, a: PNode): PNode =
+  if a.typ.isNil:
+    result = c.semOperand(c, a, {efDetermineType})
+  else:
+    result = a
+  # XXX tyGenericInst here?
+  if result.typ.kind == tyProc and hasUnresolvedParams(result, {efOperand}):
+    #and tfUnresolved in result.typ.flags:
+    let owner = result.typ.owner
+    let err =
+      # consistent error message with evaltempl/semMacroExpr
+      if owner != nil and owner.kind in {skTemplate, skMacro}:
+        errMissingGenericParamsForTemplate % a.renderTree
+      else:
+        errProcHasNoConcreteType % a.renderTree
+    localError(c.config, a.info, err)
+  considerGenSyms(c, result)
+
+proc semFinishOperands(c: PContext; n: PNode) =
+  # this needs to be called to ensure that after overloading resolution every
+  # argument has been sem'checked:
+  for i in 1..<n.len:
+    n[i] = finishOperand(c, n[i])
+
 proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
   if efNoSemCheck notin flags and n.typ != nil and n.typ.kind == tyError:
     return errorNode(c, n)
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index 64452441b..855840017 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -56,6 +56,12 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym
       elif t.kind in {tyGenericParam, tyConcept}:
         localError(c.config, a.info, errCannotInstantiateX % q.name.s)
         t = errorType(c)
+      elif isUnresolvedStatic(t) and c.inGenericContext == 0 and
+          c.matchedConcept == nil:
+        # generic/concept type bodies will try to instantiate static values but
+        # won't actually use them
+        localError(c.config, a.info, errCannotInstantiateX % q.name.s)
+        t = errorType(c)
       elif t.kind == tyGenericInvocation:
         #t = instGenericContainer(c, a, t)
         t = generateTypeInstance(c, pt, a, t)
@@ -377,10 +383,13 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   # see ttypeor.nim test.
   var i = 0
   newSeq(entry.concreteTypes, fn.typ.paramsLen+gp.len)
+  # let param instantiation know we are in a concept for unresolved statics:
+  c.matchedConcept = oldMatchedConcept
   for s in instantiateGenericParamList(c, gp, pt):
     addDecl(c, s)
     entry.concreteTypes[i] = s.typ
     inc i
+  c.matchedConcept = nil
   pushProcCon(c, result)
   instantiateProcType(c, pt, result, info)
   for _, param in paramTypes(result.typ):
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 8f3830130..557d4c447 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -2704,12 +2704,6 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int
   m.firstMismatch.arg = a
   m.firstMismatch.formal = formal
 
-proc semFinishOperands*(c: PContext, n: PNode) =
-  # this needs to be called to ensure that after overloading resolution every
-  # argument has been sem'checked:
-  for i in 1..<n.len:
-    n[i] = prepareOperand(c, n[i])
-
 proc partialMatch*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
   # for 'suggest' support:
   var marker = initIntSet()
diff --git a/tests/errmsgs/t5167_5.nim b/tests/errmsgs/t5167_5.nim
index 6c1269bce..dea7e40b3 100644
--- a/tests/errmsgs/t5167_5.nim
+++ b/tests/errmsgs/t5167_5.nim
@@ -17,9 +17,9 @@ proc bar(x: proc (x: int)) =
 let x = t #[tt.Error
         ^ 't' has unspecified generic parameters]#
 bar t #[tt.Error
-    ^ 't' has unspecified generic parameters]#
+^ type mismatch: got <template [*missing parameters*]()>]#
 
 let y = m #[tt.Error
         ^ 'm' has unspecified generic parameters]#
 bar m #[tt.Error
-    ^ 'm' has unspecified generic parameters]#
+^ type mismatch: got <macro [*missing parameters*](): untyped{.noSideEffect, gcsafe.}>]#
diff --git a/tests/generics/t23186.nim b/tests/generics/t23186.nim
new file mode 100644
index 000000000..76f38da6b
--- /dev/null
+++ b/tests/generics/t23186.nim
@@ -0,0 +1,155 @@
+# issue #23186
+
+block: # simplified
+  template typedTempl(x: int, body): untyped =
+    body
+  proc generic1[T]() =
+    discard
+  proc generic2[T]() =
+    typedTempl(1):
+      let x = generic1[T]
+  generic2[int]()
+
+import std/macros
+
+when not compiles(len((1, 2))):
+  import std/typetraits
+
+  func len(x: tuple): int =
+    arity(type(x))
+
+block: # full issue example
+  type FieldDescription = object
+    name: NimNode
+  func isTuple(t: NimNode): bool =
+    t.kind == nnkBracketExpr and t[0].kind == nnkSym and eqIdent(t[0], "tuple")
+  proc collectFieldsFromRecList(result: var seq[FieldDescription],
+                                n: NimNode,
+                                parentCaseField: NimNode = nil,
+                                parentCaseBranch: NimNode = nil,
+                                isDiscriminator = false) =
+    case n.kind
+    of nnkRecList:
+      for entry in n:
+        collectFieldsFromRecList result, entry,
+                                parentCaseField, parentCaseBranch
+    of nnkIdentDefs:
+      for i in 0 ..< n.len - 2:
+        var field: FieldDescription
+        field.name = n[i]
+        if field.name.kind == nnkPragmaExpr:
+          field.name = field.name[0]
+        if field.name.kind == nnkPostfix:
+          field.name = field.name[1]
+        result.add field
+    of nnkNilLit, nnkDiscardStmt, nnkCommentStmt, nnkEmpty:
+      discard
+    else:
+      doAssert false, "Unexpected nodes in recordFields:\n" & n.treeRepr
+  proc collectFieldsInHierarchy(result: var seq[FieldDescription],
+                                objectType: NimNode) =
+    var objectType = objectType
+    if objectType.kind == nnkRefTy:
+      objectType = objectType[0]
+    let recList = objectType[2]
+    collectFieldsFromRecList result, recList
+  proc recordFields(typeImpl: NimNode): seq[FieldDescription] =
+    let objectType = case typeImpl.kind
+      of nnkObjectTy: typeImpl
+      of nnkTypeDef: typeImpl[2]
+      else:
+        macros.error("object type expected", typeImpl)
+        return
+    collectFieldsInHierarchy(result, objectType)
+  proc skipPragma(n: NimNode): NimNode =
+    if n.kind == nnkPragmaExpr: n[0]
+    else: n
+  func declval(T: type): T =
+    doAssert false,
+      "declval should be used only in `typeof` expressions and concepts"
+    default(ptr T)[]
+  macro enumAllSerializedFieldsImpl(T: type, body: untyped): untyped =
+    var typeAst = getType(T)[1]
+    var typeImpl: NimNode
+    let isSymbol = not typeAst.isTuple
+    if not isSymbol:
+      typeImpl = typeAst
+    else:
+      typeImpl = getImpl(typeAst)
+    result = newStmtList()
+    var i = 0
+    for field in recordFields(typeImpl):
+      let
+        fieldIdent = field.name
+        realFieldName = newLit($fieldIdent.skipPragma)
+        fieldName = realFieldName
+        fieldIndex = newLit(i)
+      let fieldNameDefs =
+        if isSymbol:
+          quote:
+            const fieldName {.inject, used.} = `fieldName`
+            const realFieldName {.inject, used.} = `realFieldName`
+        else:
+          quote:
+            const fieldName {.inject, used.} = $`fieldIndex`
+            const realFieldName {.inject, used.} = $`fieldIndex`
+            # we can't access .Fieldn, so our helper knows
+            # to parseInt this
+      let field =
+        if isSymbol:
+          quote do: declval(`T`).`fieldIdent`
+        else:
+          quote do: declval(`T`)[`fieldIndex`]
+      result.add quote do:
+        block:
+          `fieldNameDefs`
+          type FieldType {.inject, used.} = type(`field`)
+          `body`
+      i += 1
+  template enumAllSerializedFields(T: type, body): untyped =
+    when T is ref|ptr:
+      type TT = type(default(T)[])
+      enumAllSerializedFieldsImpl(TT, body)
+    else:
+      enumAllSerializedFieldsImpl(T, body)
+  type
+    MemRange = object
+      startAddr: ptr byte
+      length: int
+    SszNavigator[T] = object
+      m: MemRange
+  func sszMount(data: openArray[byte], T: type): SszNavigator[T] =
+    let startAddr = unsafeAddr data[0]
+    SszNavigator[T](m: MemRange(startAddr: startAddr, length: data.len))
+  func sszMount(data: openArray[char], T: type): SszNavigator[T] =
+    let startAddr = cast[ptr byte](unsafeAddr data[0])
+    SszNavigator[T](m: MemRange(startAddr: startAddr, length: data.len))
+  template sszMount(data: MemRange, T: type): SszNavigator[T] =
+    SszNavigator[T](m: data)
+  func navigateToField[T](
+      n: SszNavigator[T],
+      FieldType: type): SszNavigator[FieldType] =
+    default(SszNavigator[FieldType])
+  type
+    FieldInfo = ref object
+      navigator: proc (m: MemRange): MemRange {.
+        gcsafe, noSideEffect, raises: [IOError] .}
+  func fieldNavigatorImpl[RecordType; FieldType; fieldName: static string](
+      m: MemRange): MemRange =
+    var typedNavigator = sszMount(m, RecordType)
+    discard navigateToField(typedNavigator, FieldType)
+    default(MemRange)
+  func genTypeInfo(T: type) =
+    when T is object:
+      enumAllSerializedFields(T):
+        discard FieldInfo(navigator: fieldNavigatorImpl[T, FieldType, fieldName])
+  type
+    Foo = object
+      bar: Bar
+    BarList = seq[uint64]
+    Bar = object
+      b: BarList
+      baz: Baz
+    Baz = object
+      i: uint64
+  genTypeInfo(Foo)
diff --git a/tests/generics/t6137.nim b/tests/generics/t6137.nim
index 991f39dd1..fb7db22f8 100644
--- a/tests/generics/t6137.nim
+++ b/tests/generics/t6137.nim
@@ -1,6 +1,6 @@
 discard """
-  errormsg: "\'vectFunc\' doesn't have a concrete type, due to unspecified generic parameters."
-  line: 28
+  errormsg: "cannot instantiate: 'T'"
+  line: 19
 """
 
 type
diff --git a/tests/generics/timplicit_and_explicit.nim b/tests/generics/timplicit_and_explicit.nim
index fe61004e4..ad0d1e88f 100644
--- a/tests/generics/timplicit_and_explicit.nim
+++ b/tests/generics/timplicit_and_explicit.nim
@@ -34,7 +34,8 @@ block: #15622
   proc test1[T](a: T, b: static[string] = "") = discard
   test1[int64](123)
   proc test2[T](a: T, b: static[string] = "") = discard
-  test2[int64, static[string]](123)
+  doAssert not (compiles do:
+    test2[int64, static[string]](123))
 
 block: #4688
   proc convertTo[T](v: int or float): T = (T)(v)
diff --git a/tests/misc/t8545.nim b/tests/misc/t8545.nim
index 89957e1d3..48b886cb8 100644
--- a/tests/misc/t8545.nim
+++ b/tests/misc/t8545.nim
@@ -1,5 +1,6 @@
 discard """
-  targets: "c cpp js"
+  # just tests that this doesn't crash the compiler
+  errormsg: "cannot instantiate: 'a:type'"
 """
 
 # bug #8545