summary refs log tree commit diff stats
path: root/compiler/semmacrosanity.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/semmacrosanity.nim')
-rw-r--r--compiler/semmacrosanity.nim126
1 files changed, 126 insertions, 0 deletions
diff --git a/compiler/semmacrosanity.nim b/compiler/semmacrosanity.nim
new file mode 100644
index 000000000..727f36470
--- /dev/null
+++ b/compiler/semmacrosanity.nim
@@ -0,0 +1,126 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2015 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Implements type sanity checking for ASTs resulting from macros. Lots of
+## room for improvement here.
+
+import ast, msgs, types, options
+
+proc ithField(n: PNode, field: var int): PSym =
+  result = nil
+  case n.kind
+  of nkRecList:
+    for i in 0..<n.len:
+      result = ithField(n[i], field)
+      if result != nil: return
+  of nkRecCase:
+    if n[0].kind != nkSym: return
+    result = ithField(n[0], field)
+    if result != nil: return
+    for i in 1..<n.len:
+      case n[i].kind
+      of nkOfBranch, nkElse:
+        result = ithField(lastSon(n[i]), field)
+        if result != nil: return
+      else: discard
+  of nkSym:
+    if field == 0: result = n.sym
+    else: dec(field)
+  else: discard
+
+proc ithField(t: PType, field: var int): PSym =
+  var base = t.baseClass
+  while base != nil:
+    let b = skipTypes(base, skipPtrs)
+    result = ithField(b.n, field)
+    if result != nil: return result
+    base = b.baseClass
+  result = ithField(t.n, field)
+
+proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
+  let x = t.skipTypes(abstractInst+{tyRange})
+  # Note: x can be unequal to t and we need to be careful to use 't'
+  # to not to skip tyGenericInst
+  case n.kind
+  of nkObjConstr:
+    let x = t.skipTypes(abstractPtrs)
+    n.typ = t
+    n[0].typ = t
+    for i in 1..<n.len:
+      var j = i-1
+      let field = x.ithField(j)
+      if field.isNil:
+        globalError conf, n.info, "invalid field at index " & $i
+      else:
+        internalAssert(conf, n[i].kind == nkExprColonExpr)
+        annotateType(n[i][1], field.typ, conf)
+  of nkPar, nkTupleConstr:
+    if x.kind == tyTuple:
+      n.typ = t
+      for i in 0..<n.len:
+        if i >= x.kidsLen: globalError conf, n.info, "invalid field at index " & $i
+        else: annotateType(n[i], x[i], conf)
+    elif x.kind == tyProc and x.callConv == ccClosure:
+      n.typ = t
+    elif x.kind == tyOpenArray: # `opcSlice` transforms slices into tuples
+      if n.kind == nkTupleConstr:
+        let
+          bracketExpr = newNodeI(nkBracket, n.info)
+          left = int n[1].intVal
+          right = int n[2].intVal
+        bracketExpr.flags = n.flags
+        case n[0].kind # is this a string slice or a array slice
+        of nkStrKinds:
+          for i in left..right:
+            bracketExpr.add newIntNode(nkCharLit, BiggestInt n[0].strVal[i])
+            annotateType(bracketExpr[^1], x.elementType, conf)
+        of nkBracket:
+          for i in left..right:
+            bracketExpr.add n[0][i]
+            annotateType(bracketExpr[^1], x.elementType, conf)
+        else:
+          globalError(conf, n.info, "Incorrectly generated tuple constr")
+        n[] = bracketExpr[]
+
+      n.typ = t
+    else:
+      globalError(conf, n.info, "() must have a tuple type")
+  of nkBracket:
+    if x.kind in {tyArray, tySequence, tyOpenArray}:
+      n.typ = t
+      for m in n: annotateType(m, x.elemType, conf)
+    else:
+      globalError(conf, n.info, "[] must have some form of array type")
+  of nkCurly:
+    if x.kind in {tySet}:
+      n.typ = t
+      for m in n: annotateType(m, x.elemType, conf)
+    else:
+      globalError(conf, n.info, "{} must have the set type")
+  of nkFloatLit..nkFloat128Lit:
+    if x.kind in {tyFloat..tyFloat128}:
+      n.typ = t
+    else:
+      globalError(conf, n.info, "float literal must have some float type")
+  of nkCharLit..nkUInt64Lit:
+    if x.kind in {tyInt..tyUInt64, tyBool, tyChar, tyEnum}:
+      n.typ = t
+    else:
+      globalError(conf, n.info, "integer literal must have some int type")
+  of nkStrLit..nkTripleStrLit:
+    if x.kind in {tyString, tyCstring}:
+      n.typ = t
+    else:
+      globalError(conf, n.info, "string literal must be of some string type")
+  of nkNilLit:
+    if x.kind in NilableTypes+{tyString, tySequence}:
+      n.typ = t
+    else:
+      globalError(conf, n.info, "nil literal must be of some pointer type")
+  else: discard