summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ast.nim2
-rw-r--r--compiler/condsyms.nim1
-rw-r--r--compiler/vm.nim21
-rw-r--r--compiler/vmdef.nim1
-rw-r--r--compiler/vmgen.nim6
-rw-r--r--lib/core/macros.nim12
-rw-r--r--tests/misc/tsizeof.nim51
7 files changed, 93 insertions, 1 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index f6778eb85..5cc608c2e 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -662,7 +662,7 @@ type
 
     mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal,
     mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNLineInfo,
-    mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mNSigHash,
+    mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mNSigHash, mNSizeOf,
     mNBindSym, mLocals, mNCallSite,
     mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl, mNGenSym,
     mNHint, mNWarning, mNError,
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index 15127e108..7ae5412f5 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -86,6 +86,7 @@ proc initDefines*(symbols: StringTableRef) =
   defineSymbol("nimHasNilSeqs")
   defineSymbol("nimHasSignatureHashInMacro")
   defineSymbol("nimHasDefault")
+  defineSymbol("nimMacrosSizealignof")
   for f in low(Feature)..high(Feature):
     defineSymbol("nimHas" & $f)
 
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 0922f7353..df0b4ac1d 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -1477,6 +1477,27 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
           regs[ra].node = opMapTypeImplToAst(c.cache, regs[rb].node.sym.typ, c.debug[pc])
         else:
           stackTrace(c, tos, pc, "node has no type")
+    of opcNGetSize:
+      decodeBImm(rkInt)
+      let n = regs[rb].node
+      case imm
+      of 0: # size
+        if n.typ == nil:
+          stackTrace(c, tos, pc, "node has no type")
+        else:
+          regs[ra].intVal = getSize(c.config, n.typ)
+      of 1: # align
+        if n.typ == nil:
+          stackTrace(c, tos, pc, "node has no type")
+        else:
+          regs[ra].intVal = getAlign(c.config, n.typ)
+      else: # offset
+        if n.kind != nkSym:
+          stackTrace(c, tos, pc, "node is not a symbol")
+        elif n.sym.kind != skField:
+          stackTrace(c, tos, pc, "symbol is not a field (nskField)")
+        else:
+          regs[ra].intVal = n.sym.offset
     of opcNStrVal:
       decodeB(rkNode)
       createStr regs[ra]
diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim
index 3b6a44deb..6099bfb9f 100644
--- a/compiler/vmdef.nim
+++ b/compiler/vmdef.nim
@@ -93,6 +93,7 @@ type
     opcNGetType,
     opcNStrVal,
     opcNSigHash,
+    opcNGetSize,
 
     opcNSetIntVal,
     opcNSetFloatVal, opcNSetSymbol, opcNSetIdent, opcNSetType, opcNSetStrVal,
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index 2d9166be0..47b2c12e5 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -1236,6 +1236,12 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     c.gABC(n, opcNGetType, dest, tmp, rc)
     c.freeTemp(tmp)
     #genUnaryABC(c, n, dest, opcNGetType)
+  of mNSizeOf:
+    let imm = case n[0].sym.name.s:
+      of "getSize": 0
+      of "getAlign": 1
+      else: 2 # "getOffset"
+    c.genUnaryABI(n, dest, opcNGetSize, imm)
   of mNStrVal: genUnaryABC(c, n, dest, opcNStrVal)
   of mNSigHash: genUnaryABC(c, n , dest, opcNSigHash)
   of mNSetIntVal:
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index b7d0f40f4..e48df38f5 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -1582,3 +1582,15 @@ proc getProjectPath*(): string = discard
   ## Returns the path to the currently compiling project, not to
   ## be confused with ``system.currentSourcePath`` which returns
   ## the path of the current module.
+
+when defined(nimMacrosSizealignof):
+  proc getSize*(arg: NimNode): int {.magic: "NSizeOf", noSideEffect.} =
+    ## Returns the same result as ``system.sizeof``, but it works on
+    ## ``NimNode`` for use in macro context.
+  proc getAlign*(arg: NimNode): int {.magic: "NSizeOf", noSideEffect.} =
+    ## Returns the same result as ``system.alignof``, but it works on
+    ## ``NimNode`` for use in macro context.
+  proc getOffset*(arg: NimNode): int {.magic: "NSizeOf", noSideEffect.} =
+    ## Returns the same result as ``system.offsetof``, but it expects
+    ## a resolved symbol node from a field of a type. Therefore it
+    ## only requires one argument instead of two.
diff --git a/tests/misc/tsizeof.nim b/tests/misc/tsizeof.nim
index c53cfb676..50d1c645a 100644
--- a/tests/misc/tsizeof.nim
+++ b/tests/misc/tsizeof.nim
@@ -3,6 +3,7 @@ discard """
 body executed
 body executed
 OK
+macros api OK
 '''
 """
 
@@ -485,3 +486,53 @@ if failed:
   quit("FAIL")
 else:
   echo "OK"
+
+##########################################
+# sizeof macros API
+##########################################
+
+import macros
+
+type
+  Vec2f = object
+    x,y: float32
+
+  Vec4f = object
+    x,y,z,w: float32
+
+  # this type is constructed to have no platform depended alignment.
+  ParticleDataA = object
+    pos, vel: Vec2f
+    birthday: float32
+    padding: float32
+    moreStuff: Vec4f
+
+const expected = [
+  # name size align offset
+  ("pos", 8, 4, 0),
+  ("vel", 8, 4, 8),
+  ("birthday", 4, 4, 16),
+  ("padding", 4, 4, 20),
+  ("moreStuff", 16, 4, 24)
+]
+
+macro typeProcessing(arg: typed): untyped =
+  let recList = arg.getTypeImpl[2]
+  recList.expectKind nnkRecList
+  for i, identDefs in recList:
+    identDefs.expectKind nnkIdentDefs
+    identDefs.expectLen 3
+    let sym = identDefs[0]
+    sym.expectKind nnkSym
+    doAssert expected[i][0] == sym.strVal
+    doAssert expected[i][1] == getSize(sym)
+    doAssert expected[i][2] == getAlign(sym)
+    doAssert expected[i][3] == getOffset(sym)
+
+  result = newCall(bindSym"echo", newLit("macros api OK"))
+
+proc main() =
+  var mylocal: ParticleDataA
+  typeProcessing(mylocal)
+
+main()