summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
authorZahary Karadjov <zahary@gmail.com>2017-04-07 23:18:14 +0300
committerZahary Karadjov <zahary@gmail.com>2017-04-08 17:28:19 +0300
commite9a3ffbc3d318911da5c46582a70288dd16275f3 (patch)
tree8b9f8c8b1cc9afc25cb7d0a9b218ea7ee4f6bcdd /compiler
parentfceef773015bd7f764c51549ec20768b5e9f5eb1 (diff)
downloadNim-e9a3ffbc3d318911da5c46582a70288dd16275f3.tar.gz
Restore the Nim's 0.14 proper handling of generic aliases
A more efficient implementation is possible by restoring the old
lifting ot tyGenericInvocation to tyGenericInst in liftTypeParam,
but this fix will suffice for now.

fixes #5087
fixes #5602
fixes #5641
fixes #5570
Diffstat (limited to 'compiler')
-rw-r--r--compiler/ast.nim3
-rw-r--r--compiler/sem.nim27
-rw-r--r--compiler/semexprs.nim2
-rw-r--r--compiler/seminst.nim3
-rw-r--r--compiler/semstmts.nim3
-rw-r--r--compiler/semtypinst.nim59
-rw-r--r--compiler/sigmatch.nim87
-rw-r--r--compiler/vm.nim6
8 files changed, 135 insertions, 55 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 49ca1c5e0..1791c742d 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -1346,6 +1346,9 @@ proc initIdTable*(x: var TIdTable) =
   x.counter = 0
   newSeq(x.data, StartSize)
 
+proc newIdTable*: TIdTable =
+  initIdTable(result)
+
 proc resetIdTable*(x: var TIdTable) =
   x.counter = 0
   # clear and set to old initial size:
diff --git a/compiler/sem.nim b/compiler/sem.nim
index ab3c5cec8..9f80e1399 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -351,8 +351,8 @@ when false:
     for i in 0 ..< n.safeLen:
       resetSemFlag(n[i])
 
-proc semAfterMacroCall(c: PContext, n: PNode, s: PSym,
-                       flags: TExprFlags): PNode =
+proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
+                       s: PSym, flags: TExprFlags): PNode =
   ## Semantically check the output of a macro.
   ## This involves processes such as re-checking the macro output for type
   ## coherence, making sure that variables declared with 'let' aren't
@@ -363,8 +363,8 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym,
     globalError(s.info, errTemplateInstantiationTooNested)
   c.friendModules.add(s.owner.getModule)
 
-  result = n
-  excl(n.flags, nfSem)
+  result = macroResult
+  excl(result.flags, nfSem)
   #resetSemFlag n
   if s.typ.sons[0] == nil:
     result = semStmt(c, result)
@@ -378,13 +378,26 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym,
     of tyStmt:
       result = semStmt(c, result)
     of tyTypeDesc:
-      if n.kind == nkStmtList: result.kind = nkStmtListType
+      if result.kind == nkStmtList: result.kind = nkStmtListType
       var typ = semTypeNode(c, result, nil)
       result.typ = makeTypeDesc(c, typ)
       #result = symNodeFromType(c, typ, n.info)
     else:
+      var retType = s.typ.sons[0]
+      if s.ast[genericParamsPos] != nil and retType.isMetaType:
+        # The return type may depend on the Macro arguments
+        # e.g. template foo(T: typedesc): seq[T]
+        # We will instantiate the return type here, because
+        # we now know the supplied arguments
+        var paramTypes = newIdTable()
+        for param, value in genericParamsInMacroCall(s, call):
+          idTablePut(paramTypes, param.typ, value.typ)
+
+        retType = generateTypeInstance(c, paramTypes,
+                                       macroResult.info, retType)
+
       result = semExpr(c, result, flags)
-      result = fitNode(c, s.typ.sons[0], result, result.info)
+      result = fitNode(c, retType, result, result.info)
       #GlobalError(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0]))
   dec(evalTemplateCounter)
   discard c.friendModules.pop()
@@ -409,7 +422,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
   #  c.evalContext = c.createEvalContext(emStatic)
   result = evalMacroCall(c.module, c.cache, n, nOrig, sym)
   if efNoSemCheck notin flags:
-    result = semAfterMacroCall(c, result, sym, flags)
+    result = semAfterMacroCall(c, n, result, sym, flags)
   result = wrapInComesFrom(nOrig.info, result)
   popInfoContext()
 
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 1b2222b2e..fff16092c 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -16,7 +16,7 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
   styleCheckUse(n.info, s)
   pushInfoContext(n.info)
   result = evalTemplate(n, s, getCurrOwner(c), efFromHlo in flags)
-  if efNoSemCheck notin flags: result = semAfterMacroCall(c, result, s, flags)
+  if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, s, flags)
   popInfoContext()
 
 proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index 874be8dd6..b5dca4c1b 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -36,7 +36,8 @@ proc rawPushProcCon(c: PContext, owner: PSym) =
   c.p = x
 
 proc rawHandleSelf(c: PContext; owner: PSym) =
-  if c.selfName != nil and owner.kind in {skProc, skMethod, skConverter, skIterator, skMacro} and owner.typ != nil:
+  const callableSymbols = {skProc, skMethod, skConverter, skIterator, skMacro}
+  if c.selfName != nil and owner.kind in callableSymbols and owner.typ != nil:
     let params = owner.typ.n
     if params.len > 1:
       let arg = params[1].sym
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index a1c3bd001..13670b0d3 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -1177,7 +1177,8 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
       var objB = t.sons[2]
       while true:
         if objB.kind == tyGenericBody: objB = objB.lastSon
-        elif objB.kind == tyGenericInvocation: objB = objB.sons[0]
+        elif objB.kind in {tyGenericInvocation, tyGenericInst}:
+          objB = objB.sons[0]
         else: break
       if obj.kind in {tyObject, tyDistinct} and sameType(obj, objB):
         if obj.assignment.isNil:
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index 27b154d62..9408a6d3d 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -307,31 +307,32 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
   rawAddSon(result, newbody)
   checkPartialConstructedType(cl.info, newbody)
   let dc = newbody.deepCopy
-  if dc != nil and sfFromGeneric notin newbody.deepCopy.flags:
-    # 'deepCopy' needs to be instantiated for
-    # generics *when the type is constructed*:
-    newbody.deepCopy = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info,
-                                            attachedDeepCopy, 1)
-  if bodyIsNew and newbody.typeInst == nil:
-    #doassert newbody.typeInst == nil
-    newbody.typeInst = result
-    if tfRefsAnonObj in newbody.flags and newbody.kind != tyGenericInst:
-      # can come here for tyGenericInst too, see tests/metatype/ttypeor.nim
-      # need to look into this issue later
-      assert newbody.kind in {tyRef, tyPtr}
-      assert newbody.lastSon.typeInst == nil
-      newbody.lastSon.typeInst = result
-  let asgn = newbody.assignment
-  if asgn != nil and sfFromGeneric notin asgn.flags:
-    # '=' needs to be instantiated for generics when the type is constructed:
-    newbody.assignment = cl.c.instTypeBoundOp(cl.c, asgn, result, cl.info,
-                                              attachedAsgn, 1)
-  let methods = skipTypes(bbody, abstractPtrs).methods
-  for col, meth in items(methods):
-    # we instantiate the known methods belonging to that type, this causes
-    # them to be registered and that's enough, so we 'discard' the result.
-    discard cl.c.instTypeBoundOp(cl.c, meth, result, cl.info,
-      attachedAsgn, col)
+  if cl.allowMetaTypes == false:
+    if dc != nil and sfFromGeneric notin newbody.deepCopy.flags:
+      # 'deepCopy' needs to be instantiated for
+      # generics *when the type is constructed*:
+      newbody.deepCopy = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info,
+                                              attachedDeepCopy, 1)
+    if bodyIsNew and newbody.typeInst == nil:
+      #doassert newbody.typeInst == nil
+      newbody.typeInst = result
+      if tfRefsAnonObj in newbody.flags and newbody.kind != tyGenericInst:
+        # can come here for tyGenericInst too, see tests/metatype/ttypeor.nim
+        # need to look into this issue later
+        assert newbody.kind in {tyRef, tyPtr}
+        assert newbody.lastSon.typeInst == nil
+        newbody.lastSon.typeInst = result
+    let asgn = newbody.assignment
+    if asgn != nil and sfFromGeneric notin asgn.flags:
+      # '=' needs to be instantiated for generics when the type is constructed:
+      newbody.assignment = cl.c.instTypeBoundOp(cl.c, asgn, result, cl.info,
+                                                attachedAsgn, 1)
+    let methods = skipTypes(bbody, abstractPtrs).methods
+    for col, meth in items(methods):
+      # we instantiate the known methods belonging to that type, this causes
+      # them to be registered and that's enough, so we 'discard' the result.
+      discard cl.c.instTypeBoundOp(cl.c, meth, result, cl.info,
+        attachedAsgn, col)
 
 proc eraseVoidParams*(t: PType) =
   # transform '(): void' into '()' because old parts of the compiler really
@@ -526,6 +527,14 @@ proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo,
   result = replaceTypeVarsT(cl, t)
   popInfoContext()
 
+proc prepareMetatypeForSigmatch*(p: PContext, pt: TIdTable, info: TLineInfo,
+                                 t: PType): PType =
+  var cl = initTypeVars(p, pt, info, nil)
+  cl.allowMetaTypes = true
+  pushInfoContext(info)
+  result = replaceTypeVarsT(cl, t)
+  popInfoContext()
+
 template generateTypeInstance*(p: PContext, pt: TIdTable, arg: PNode,
                                t: PType): untyped =
   generateTypeInstance(p, pt, arg.info, t)
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 4661abda0..56e12df03 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -26,7 +26,7 @@ type
     sym*: PSym
     unmatchedVarParam*: int
     diagnostics*: seq[string]
-  
+
   CandidateErrors* = seq[CandidateError]
 
   TCandidate* = object
@@ -68,7 +68,7 @@ type
                               # future.
     mutabilityProblem*: uint8 # tyVar mismatch
     inheritancePenalty: int  # to prefer closest father object type
-  
+
   TTypeRelation* = enum      # order is important!
     isNone, isConvertible,
     isIntConv,
@@ -639,7 +639,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
                         makeTypeDesc(c, typ)
 
         typeParams.safeAdd((param, typ))
-      
+
       addDecl(c, param)
 
   for param in typeClass.n[0]:
@@ -676,7 +676,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
     flags: TExprFlags = {}
     collectDiagnostics = m.diagnostics != nil or
                          sfExplain in typeClass.sym.flags
-  
+
   if collectDiagnostics:
     oldWriteHook = writelnHook
     # XXX: we can't write to m.diagnostics directly, because
@@ -688,13 +688,13 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
       let msg = s.replace("Error:", errorPrefix)
       if oldWriteHook != nil: oldWriteHook msg
       diagnostics.add msg
-   
+
   var checkedBody = c.semTryExpr(c, body.copyTree, flags)
 
   if collectDiagnostics:
     writelnHook = oldWriteHook
     for msg in diagnostics: m.diagnostics.safeAdd msg
-  
+
   if checkedBody == nil: return nil
 
   # The inferrable type params have been identified during the semTryExpr above.
@@ -746,7 +746,7 @@ proc inferStaticParam*(lhs: PNode, rhs: BiggestInt): PType =
   #
   # Preconditions:
   #
-  #   * The input of this proc must be semantized 
+  #   * The input of this proc must be semantized
   #     - all templates should be expanded
   #     - aby constant folding possible should already be performed
   #
@@ -769,13 +769,13 @@ proc inferStaticParam*(lhs: PNode, rhs: BiggestInt): PType =
         return inferStaticParam(lhs[2], rhs - lhs[1].intVal)
       elif lhs[2].kind == nkIntLit:
         return inferStaticParam(lhs[1], rhs - lhs[2].intVal)
-    
+
     of mDec, mSubI, mSubU, mPred:
       if lhs[1].kind == nkIntLit:
         return inferStaticParam(lhs[2], lhs[1].intVal - rhs)
       elif lhs[2].kind == nkIntLit:
         return inferStaticParam(lhs[1], rhs + lhs[2].intVal)
-    
+
     of mMulI, mMulU:
       if lhs[1].kind == nkIntLit:
         if rhs mod lhs[1].intVal == 0:
@@ -783,34 +783,34 @@ proc inferStaticParam*(lhs: PNode, rhs: BiggestInt): PType =
       elif lhs[2].kind == nkIntLit:
         if rhs mod lhs[2].intVal == 0:
           return inferStaticParam(lhs[1], rhs div lhs[2].intVal)
-    
+
     of mDivI, mDivU:
       if lhs[1].kind == nkIntLit:
         if lhs[1].intVal mod rhs == 0:
           return inferStaticParam(lhs[2], lhs[1].intVal div rhs)
       elif lhs[2].kind == nkIntLit:
         return inferStaticParam(lhs[1], lhs[2].intVal * rhs)
-    
+
     of mShlI:
       if lhs[2].kind == nkIntLit:
         return inferStaticParam(lhs[1], rhs shr lhs[2].intVal)
-    
+
     of mShrI:
       if lhs[2].kind == nkIntLit:
         return inferStaticParam(lhs[1], rhs shl lhs[2].intVal)
-    
+
     of mUnaryMinusI:
       return inferStaticParam(lhs[1], -rhs)
-    
+
     of mUnaryPlusI, mToInt, mToBiggestInt:
       return inferStaticParam(lhs[1], rhs)
-    
+
     else: discard
-  
+
   elif lhs.kind == nkSym and lhs.typ.kind == tyStatic and lhs.typ.n == nil:
     lhs.typ.n = newIntNode(nkIntLit, rhs)
     return lhs.typ
-  
+
   return nil
 
 proc failureToInferStaticParam(n: PNode) =
@@ -825,7 +825,7 @@ proc inferStaticsInRange(c: var TCandidate,
                                           allowUnresolved = true)
   let upperBound = tryResolvingStaticExpr(c, inferred.n[1],
                                           allowUnresolved = true)
-  
+
   template doInferStatic(c: var TCandidate, e: PNode, r: BiggestInt) =
     var exp = e
     var rhs = r
@@ -1049,7 +1049,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
       result = typeRel(c, f.sons[1].skipTypes({tyTypeDesc}),
                           a.sons[1].skipTypes({tyTypeDesc}))
       if result < isGeneric: return isNone
-      
+
       if fRange.rangeHasUnresolvedStatic:
         return inferStaticsInRange(c, fRange, a)
       elif c.c.inTypeClass > 0 and aRange.rangeHasUnresolvedStatic:
@@ -1204,9 +1204,49 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
   of tyEmpty, tyVoid:
     if a.kind == f.kind: result = isEqual
 
-  of tyGenericInst, tyAlias:
+  of tyAlias:
     result = typeRel(c, lastSon(f), a)
 
+  of tyGenericInst:
+    var prev = PType(idTableGet(c.bindings, f))
+    var f = if prev == nil: f else: prev
+
+    let roota = a.skipGenericAlias
+    let rootf = f.skipGenericAlias
+
+    var m = c
+    if a.kind == tyGenericInst:
+      if roota.base == rootf.base:
+        for i in 1 .. rootf.sonsLen-2:
+          let ff = rootf.sons[i]
+          let aa = roota.sons[i]
+          result = typeRel(c, ff, aa)
+          if result notin {isEqual, isGeneric}: return isNone
+          # if ff.kind == tyRange and result != isEqual: return isNone
+
+        if prev == nil: put(c, f, a)
+        result = isGeneric
+      else:
+        var aAsObject = roota.lastSon
+        if rootf.lastSon.kind in {tyAnd, tyOr}:
+          result = typeRel(c, lastSon(f), a)
+          if result != isNone: put(c, f, a)
+          return
+
+        if rootf.lastSon.kind == tyRef and aAsObject.kind == tyRef:
+          aAsObject = aAsObject.base
+
+        if aAsObject.kind == tyObject:
+          let baseType = aAsObject.base
+          if baseType != nil:
+            c.inheritancePenalty += 1
+            return typeRel(c, f, baseType)
+
+        result = isNone
+    else:
+      result = typeRel(c, lastSon(f), a)
+      if result != isNone: put(c, f, a)
+
   of tyGenericBody:
     considerPreviousT:
       if a.kind == tyGenericInst and a.sons[0] == f:
@@ -1217,6 +1257,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
 
   of tyGenericInvocation:
     var x = a.skipGenericAlias
+    
+    # XXX: This is very hacky. It should be moved back into liftTypeParam
+    if x.kind == tyGenericInst and c.calleeSym != nil and c.calleeSym.kind == skProc:
+      let inst = prepareMetatypeForSigmatch(c.c, c.bindings, c.call.info, f)
+      return typeRel(c, inst, a)
+
     var depth = 0
     if x.kind == tyGenericInvocation or f.sons[0].kind != tyGenericBody:
       #InternalError("typeRel: tyGenericInvocation -> tyGenericInvocation")
@@ -2086,6 +2132,7 @@ proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo;
     localError(info, errGenerated, "cannot instantiate '" & dc.name.s & "'")
     return nil
   var f = dc.typ.sons[col]
+
   if op == attachedDeepCopy:
     if f.kind in {tyRef, tyPtr}: f = f.lastSon
   else:
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 6a9545193..14800fb13 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -1606,6 +1606,12 @@ proc setupMacroParam(x: PNode, typ: PType): TFullReg =
     n.typ = x.typ
     result.node = n
 
+iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) =
+  let gp = macroSym.ast[genericParamsPos]
+  for i in 0 .. <gp.len:
+    let idx = macroSym.typ.len + i
+    yield (gp[i].sym, call.sons[idx])
+
 var evalMacroCounter: int
 
 proc evalMacroCall*(module: PSym; cache: IdentCache, n, nOrig: PNode,