summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/ast.nim3
-rw-r--r--compiler/evals.nim32
-rw-r--r--compiler/sem.nim6
-rw-r--r--compiler/semdata.nim6
-rw-r--r--compiler/semexprs.nim51
-rw-r--r--compiler/semtypes.nim16
-rw-r--r--compiler/sigmatch.nim62
7 files changed, 135 insertions, 41 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 49e050f6c..04ac88c1c 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -689,6 +689,7 @@ type
                               # for enum types a list of symbols
                               # for tyInt it can be the int literal
                               # for procs and tyGenericBody, it's the
+                              # the body of the user-defined type class
                               # formal param list
                               # else: unused
     destructor*: PSym         # destructor. warning: nil here may not necessary
@@ -701,6 +702,7 @@ type
                               # -1 means that the size is unkwown
     align*: int               # the type's alignment requirements
     loc*: TLoc
+    testeeName*: PIdent       # the test variable in user-defined type classes
 
   TPair*{.final.} = object 
     key*, val*: PObject
@@ -1075,6 +1077,7 @@ proc assignType(dest, src: PType) =
   dest.size = src.size
   dest.align = src.align
   dest.destructor = src.destructor
+  dest.testeeName = src.testeeName
   # this fixes 'type TLock = TSysLock':
   if src.sym != nil:
     if dest.sym != nil:
diff --git a/compiler/evals.nim b/compiler/evals.nim
index 053068ea4..7bd74b04a 100644
--- a/compiler/evals.nim
+++ b/compiler/evals.nim
@@ -53,7 +53,8 @@ type
     features: TSandboxFlags
     globals*: TIdNodeTable    # state of global vars
     getType*: proc(n: PNode): PNode {.closure.}
-  
+    handleIsOperator*: proc(n: PNode): PNode {.closure.}
+
   PEvalContext* = ref TEvalContext
 
   TEvalFlag = enum 
@@ -916,33 +917,6 @@ proc evalTypeTrait*(trait, operand: PNode, context: PSym): PNode =
   else:
     internalAssert false
 
-proc evalIsOp*(n: PNode): PNode =
-  InternalAssert n.sonsLen == 3 and
-    n[1].kind == nkSym and n[1].sym.kind == skType and
-    n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
-  
-  let t1 = n[1].sym.typ
-
-  if n[2].kind in {nkStrLit..nkTripleStrLit}:
-    case n[2].strVal.normalize
-    of "closure":
-      let t = skipTypes(t1, abstractRange)
-      result = newIntNode(nkIntLit, ord(t.kind == tyProc and
-                                        t.callConv == ccClosure and 
-                                        tfIterator notin t.flags))
-    of "iterator":
-      let t = skipTypes(t1, abstractRange)
-      result = newIntNode(nkIntLit, ord(t.kind == tyProc and
-                                        t.callConv == ccClosure and 
-                                        tfIterator in t.flags))
-  else:
-    let t2 = n[2].typ
-    var match = if t2.kind == tyTypeClass: matchTypeClass(t2, t1)
-                else: sameType(t1, t2)
-    result = newIntNode(nkIntLit, ord(match))
-
-  result.typ = n.typ
-
 proc expectString(n: PNode) =
   if n.kind notin nkStrKinds:
     GlobalError(n.info, errStringLiteralExpected)
@@ -1038,7 +1012,7 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
     result = evalTypeTrait(n[0], operand, c.module)
   of mIs:
     n.sons[1] = evalAux(c, n.sons[1], {})
-    result = evalIsOp(n)
+    result = c.handleIsOperator(n)
   of mSlurp: result = evalSlurp(evalAux(c, n.sons[1], {}), c.module)
   of mStaticExec:
     let cmd = evalAux(c, n.sons[1], {})
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 4396a9093..c411d8ac4 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -198,6 +198,8 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode =
       #GlobalError(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0]))
   dec(evalTemplateCounter)
 
+proc IsOpImpl(c: PContext, n: PNode): PNode
+
 proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, 
                   semCheck: bool = true): PNode = 
   markUsed(n, sym)
@@ -215,6 +217,9 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
       else:
         result = symNodeFromType(c, e.typ, n.info)
 
+    c.evalContext.handleIsOperator = proc (n: PNode): PNode =
+      result = IsOpImpl(c, n)
+
   result = evalMacroCall(c.evalContext, n, nOrig, sym)
   if semCheck: result = semAfterMacroCall(c, result, sym)
 
@@ -250,6 +255,7 @@ proc myOpen(module: PSym): PPassContext =
   if c.p != nil: InternalError(module.info, "sem.myOpen")
   c.semConstExpr = semConstExpr
   c.semExpr = semExpr
+  c.semTryExpr = tryExpr
   c.semOperand = semOperand
   c.semConstBoolExpr = semConstBoolExpr
   c.semOverloadedCall = semOverloadedCall
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index 8b04f4af5..4c066f5fa 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -72,6 +72,7 @@ type
     libs*: TLinkedList         # all libs used by this module
     semConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # for the pragmas
     semExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
+    semTryExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
     semOperand*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
     semConstBoolExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # XXX bite the bullet
     semOverloadedCall*: proc (c: PContext, n, nOrig: PNode,
@@ -204,6 +205,11 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType =
   result = newTypeS(tyTypeDesc, c)
   result.addSonSkipIntLit(typ.AssertNotNil)
 
+proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
+  let typedesc = makeTypeDesc(c, typ)
+  let sym = newSym(skType, idAnon, getCurrOwner(), info).linkTo(typedesc)
+  return newSymNode(sym, info)
+
 proc newTypeS(kind: TTypeKind, c: PContext): PType = 
   result = newType(kind, getCurrOwner())
 
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index ebda31501..d80a1cb5a 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -295,6 +295,39 @@ proc semOf(c: PContext, n: PNode): PNode =
   n.typ = getSysType(tyBool)
   result = n
 
+proc IsOpImpl(c: PContext, n: PNode): PNode =
+  InternalAssert n.sonsLen == 3 and
+    n[1].kind == nkSym and n[1].sym.kind == skType and
+    n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
+  
+  let t1 = n[1].sym.typ.skipTypes({tyTypeDesc})
+
+  if n[2].kind in {nkStrLit..nkTripleStrLit}:
+    case n[2].strVal.normalize
+    of "closure":
+      let t = skipTypes(t1, abstractRange)
+      result = newIntNode(nkIntLit, ord(t.kind == tyProc and
+                                        t.callConv == ccClosure and 
+                                        tfIterator notin t.flags))
+    of "iterator":
+      let t = skipTypes(t1, abstractRange)
+      result = newIntNode(nkIntLit, ord(t.kind == tyProc and
+                                        t.callConv == ccClosure and 
+                                        tfIterator in t.flags))
+  else:
+    var match: bool
+    let t2 = n[2].typ
+    if t2.kind == tyTypeClass:
+      var m: TCandidate
+      InitCandidate(m, t2)
+      match = matchUserTypeClass(c, m, emptyNode, t2, t1) != nil
+    else:
+      match = sameType(t1, t2)
+ 
+    result = newIntNode(nkIntLit, ord(match))
+
+  result.typ = n.typ
+
 proc semIs(c: PContext, n: PNode): PNode =
   if sonsLen(n) != 3:
     LocalError(n.info, errXExpectsTwoArguments, "is")
@@ -303,21 +336,21 @@ proc semIs(c: PContext, n: PNode): PNode =
   n.typ = getSysType(tyBool)
   
   n.sons[1] = semExprWithType(c, n[1], {efDetermineType})
-  if n[1].typ.kind != tyTypeDesc:
-    LocalError(n[0].info, errTypeExpected)
-
+  
   if n[2].kind notin {nkStrLit..nkTripleStrLit}:
     let t2 = semTypeNode(c, n[2], nil)
     n.sons[2] = newNodeIT(nkType, n[2].info, t2)
 
-  if n[1].typ.sonsLen == 0:
+  if n[1].typ.kind != tyTypeDesc:
+    n.sons[1] = makeTypeSymNode(c, n[1].typ, n[1].info)
+  elif n[1].typ.sonsLen == 0:
     # this is a typedesc variable, leave for evals
     return
-  else:
-    let t1 = n[1].typ.sons[0]
-    # BUGFIX: don't evaluate this too early: ``T is void``
-    if not containsGenericType(t1): result = evalIsOp(n)
-  
+
+  let t1 = n[1].typ.sons[0]
+  # BUGFIX: don't evaluate this too early: ``T is void``
+  if not containsGenericType(t1): result = IsOpImpl(c, n)
+
 proc semOpAux(c: PContext, n: PNode) =
   const flags = {efDetermineType}
   for i in countup(1, n.sonsLen-1):
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index b02fa7c31..e9541997f 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -871,6 +871,21 @@ proc freshType(res, prev: PType): PType {.inline.} =
   else:
     result = res
 
+proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
+  # if n.sonsLen == 0: return newConstraint(c, tyTypeClass)
+  result = newOrPrevType(tyTypeClass, prev, c)
+  result.testeeName = considerAcc(n[0])
+  result.n = n[3]
+
+  let
+    pragmas = n[1]
+    inherited = n[2]
+    
+  if inherited.kind != nkEmpty:
+    for n in inherited.sons:
+      let typ = semTypeNode(c, n, nil)
+      result.sons.safeAdd(typ)
+
 proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
   result = nil
   if gCmd == cmdIdeTools: suggestExpr(c, n)
@@ -973,6 +988,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
       result = newOrPrevType(tyError, prev, c)
   of nkObjectTy: result = semObjectNode(c, n, prev)
   of nkTupleTy: result = semTuple(c, n, prev)
+  of nkTypeClassTy: result = semTypeClass(c, n, prev)
   of nkRefTy: result = semAnyRef(c, n, tyRef, prev)
   of nkPtrTy: result = semAnyRef(c, n, tyPtr, prev)
   of nkVarTy: result = semVarType(c, n, prev)
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 5766aa164..25d0d8004 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -40,7 +40,9 @@ type
                              # be instantiated
     typedescMatched: bool
     inheritancePenalty: int  # to prefer closest father object type
-  
+    errors*: seq[string]     # additional clarifications to be displayed to the
+                             # user if overload resolution fails
+
   TTypeRelation* = enum      # order is important!
     isNone, isConvertible,
     isIntConv,
@@ -750,11 +752,55 @@ proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType,
       result.typ = getInstantiatedType(c, arg, m, base(f))
     m.baseTypeMatch = true
 
+proc matchUserTypeClass*(c: PContext, m: var TCandidate,
+                         arg: PNode, f, a: PType): PNode =
+  if f.n == nil:
+    let r = typeRel(m, f, a)
+    return if r == isGeneric: arg else: nil
+ 
+  var prev = PType(idTableGet(m.bindings, f))
+  if prev != nil:
+    if sameType(prev, a): return arg
+    else: return nil
+
+  # pushInfoContext(arg.info)
+  openScope(c)
+
+  var testee = newSym(skParam, f.testeeName, f.sym, f.sym.info)
+  testee.typ = a
+  addDecl(c, testee)
+
+  for stmt in f.n:
+    var e = c.semTryExpr(c, copyTree(stmt))
+    if e == nil:
+      let expStr = renderTree(stmt, {renderNoComments})
+      m.errors.safeAdd("can't compile " & expStr & "  for " & a.typeToString)
+      return nil
+    case e.kind
+    of nkReturnStmt:
+      nil
+    of nkTypeSection: nil
+    of nkConstDef: nil
+    else:
+      if e.typ.kind == tyBool:
+        let verdict = c.semConstExpr(c, e)
+        if verdict.intVal == 0:
+          let expStr = renderTree(stmt, {renderNoComments})
+          m.errors.safeAdd(expStr & " doesn't hold for " & a.typeToString)
+          return nil
+
+  closeScope(c)
+
+  result = arg
+  put(m.bindings, f, a)
+
 proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType, 
-                        arg, argOrig: PNode): PNode =
+                        argSemantized, argOrig: PNode): PNode =
+  var arg = argSemantized
   var r: TTypeRelation
   let fMaybeExpr = f.skipTypes({tyDistinct})
-  if fMaybeExpr.kind == tyExpr:
+  case fMaybeExpr.kind
+  of tyExpr:
     if fMaybeExpr.sonsLen == 0:
       r = isGeneric
     else:
@@ -776,6 +822,16 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType,
         
     if r == isGeneric:
       put(m.bindings, f, arg.typ)
+  of tyTypeClass:
+    if fMaybeExpr.n != nil:
+      let match = matchUserTypeClass(c, m, arg, fMaybeExpr, a)
+      if match != nil:
+        r = isGeneric
+        arg = match
+      else:
+        r = isNone
+    else:
+      r = typeRel(m, f, a)
   else:
     r = typeRel(m, f, a)