summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ast.nim10
-rw-r--r--compiler/canonicalizer.nim2
-rw-r--r--compiler/condsyms.nim1
-rw-r--r--compiler/jsgen.nim2
-rw-r--r--compiler/pragmas.nim4
-rw-r--r--compiler/semexprs.nim15
-rw-r--r--compiler/semfold.nim12
-rw-r--r--compiler/semmagic.nim57
-rw-r--r--compiler/semtypes.nim1
-rw-r--r--compiler/sigmatch.nim1
-rw-r--r--compiler/sizealignoffsetimpl.nim447
-rw-r--r--compiler/types.nim192
-rw-r--r--compiler/vmgen.nim4
-rw-r--r--lib/system.nim21
-rw-r--r--testament/tester.nim2
-rw-r--r--tests/misc/tsizeof.nim348
-rw-r--r--tests/misc/tsizeof2.nim11
-rw-r--r--tests/types/tillegalrecursion3.nim12
18 files changed, 906 insertions, 236 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index ebb5937e0..f5575d8e5 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -582,7 +582,8 @@ type
   TMagic* = enum # symbols that require compiler magic:
     mNone,
     mDefined, mDefinedInScope, mCompiles, mArrGet, mArrPut, mAsgn,
-    mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mType, mTypeOf,
+    mLow, mHigh, mSizeOf, mAlignOf, mOffsetOf, mTypeTrait,
+    mIs, mOf, mAddr, mType, mTypeOf,
     mRoof, mPlugin, mEcho, mShallowCopy, mSlurp, mStaticExec, mStatic,
     mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst,
     mUnaryLt, mInc, mDec, mOrd,
@@ -698,11 +699,6 @@ const
     mConStrStr, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
     mInRange, mInSet, mRepr,
     mCopyStr, mCopyStrLast}
-  # magics that require special semantic checking and
-  # thus cannot be overloaded (also documented in the spec!):
-  SpecialSemMagics* = {
-    mDefined, mDefinedInScope, mCompiles, mLow, mHigh, mSizeOf, mIs, mOf,
-    mShallowCopy, mExpandToAst, mParallel, mSpawn, mAstToStr}
 
 type
   PNode* = ref TNode
@@ -1274,7 +1270,7 @@ proc newType*(kind: TTypeKind, owner: PSym): PType =
   result.kind = kind
   result.owner = owner
   result.size = -1
-  result.align = 2            # default alignment
+  result.align = -1            # default alignment
   result.id = getID()
   result.lockLevel = UnspecifiedLockLevel
   when debugIds:
diff --git a/compiler/canonicalizer.nim b/compiler/canonicalizer.nim
index d1669a06c..2b6096298 100644
--- a/compiler/canonicalizer.nim
+++ b/compiler/canonicalizer.nim
@@ -300,7 +300,7 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) =
   if t.size != - 1:
     add(result, '/')
     encodeVBiggestInt(t.size, result)
-  if t.align != 2:
+  if t.align != - 1:
     add(result, '=')
     encodeVInt(t.align, result)
   encodeLoc(w, t.loc, result)
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index f353196d8..13e0d0d11 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -58,6 +58,7 @@ proc initDefines*(symbols: StringTableRef) =
   defineSymbol("nimtypedescfixed")
   defineSymbol("nimKnowsNimvm")
   defineSymbol("nimArrIdx")
+  defineSymbol("nimHasalignOf")
   defineSymbol("nimImmediateDeprecated")
   defineSymbol("nimNewShiftOps")
   defineSymbol("nimDistros")
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 7b30a0009..7ab4a0472 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -1759,7 +1759,6 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
   of mIsNil: unaryExpr(p, n, r, "", "($1 === null)")
   of mEnumToStr: genRepr(p, n, r)
   of mNew, mNewFinalize: genNew(p, n)
-  of mSizeOf: r.res = rope(getSize(p.config, n.sons[1].typ))
   of mChr, mArrToSeq: gen(p, n.sons[1], r)      # nothing to do
   of mOrd: genOrd(p, n, r)
   of mLengthStr, mLengthSeq, mLengthOpenArray, mLengthArray:
@@ -2356,4 +2355,3 @@ proc myOpen(graph: ModuleGraph; s: PSym): PPassContext =
   result = newModule(graph, s)
 
 const JSgenPass* = makePass(myOpen, myProcess, myClose)
-
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index eda52ab02..c69da1813 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -803,9 +803,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
         if sym.typ == nil: invalidPragma(c, it)
         var size = expectIntLit(c, it)
         if not isPowerOfTwo(size) or size <= 0 or size > 8:
-          localError(c.config, it.info, "power of two expected")
+          localError(c.config, it.info, "size may only be 1, 2, 4 or 8")
         else:
           sym.typ.size = size
+          # TODO, this is not correct
+          sym.typ.align = int16(size)
       of wNodecl:
         noVal(c, it)
         incl(sym.loc.flags, lfNoDecl)
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index abd6d1094..a71796de2 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -158,6 +158,10 @@ proc isCastable(conf: ConfigRef; dst, src: PType): bool =
   var dstSize, srcSize: BiggestInt
   dstSize = computeSize(conf, dst)
   srcSize = computeSize(conf, src)
+  if dstSize == -3 or srcSize == -3: # szUnknownSize
+    # The Nim compiler can't detect if it's legal or not.
+    # Just assume the programmer knows what he is doing.
+    return true
   if dstSize < 0:
     result = false
   elif srcSize < 0:
@@ -308,15 +312,6 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
       localError(c.config, n.info, "invalid argument for: " & opToStr[m])
   result = n
 
-proc semSizeof(c: PContext, n: PNode): PNode =
-  if sonsLen(n) != 2:
-    localError(c.config, n.info, errXExpectsTypeOrValue % "sizeof")
-  else:
-    n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType})
-    #restoreOldStyleType(n.sons[1])
-  n.typ = getSysType(c.graph, n.info, tyInt)
-  result = n
-
 proc fixupStaticType(c: PContext, n: PNode) =
   # This proc can be applied to evaluated expressions to assign
   # them a static type.
@@ -1958,7 +1953,6 @@ proc setMs(n: PNode, s: PSym): PNode =
 
 proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
   # this is a hotspot in the compiler!
-  # DON'T forget to update ast.SpecialSemMagics if you add a magic here!
   result = n
   case s.magic # magics that need special treatment
   of mAddr:
@@ -1975,7 +1969,6 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
   of mCompiles: result = semCompiles(c, setMs(n, s), flags)
   #of mLow: result = semLowHigh(c, setMs(n, s), mLow)
   #of mHigh: result = semLowHigh(c, setMs(n, s), mHigh)
-  of mSizeOf: result = semSizeof(c, setMs(n, s))
   of mIs: result = semIs(c, setMs(n, s), flags)
   #of mOf: result = semOf(c, setMs(n, s))
   of mShallowCopy: result = semShallowCopy(c, n, flags)
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index e977a242b..92305fd11 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -646,18 +646,6 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode =
       of mNone:
         # If it has no sideEffect, it should be evaluated. But not here.
         return
-      of mSizeOf:
-        var a = n.sons[1]
-        if computeSize(g.config, a.typ) < 0:
-          localError(g.config, a.info, "cannot evaluate 'sizeof' because its type is not defined completely")
-          result = nil
-        elif skipTypes(a.typ, typedescInst+{tyRange, tyArray}).kind in
-             IntegralTypes+NilableTypes+{tySet}:
-          #{tyArray,tyObject,tyTuple}:
-          result = newIntNodeT(getSize(g.config, a.typ), n, g)
-        else:
-          result = nil
-          # XXX: size computation for complex types is still wrong
       of mLow:
         result = newIntNodeT(firstOrd(g.config, n.sons[1].typ), n, g)
       of mHigh:
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index 8bfa5545e..5ab4d25e0 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -307,6 +307,14 @@ proc semOf(c: PContext, n: PNode): PNode =
 
 proc magicsAfterOverloadResolution(c: PContext, n: PNode,
                                    flags: TExprFlags): PNode =
+  ## This is the preferred code point to implement magics.
+  ## This function basically works like a macro, with the difference
+  ## that it is implemented in the compiler and not on the nimvm.
+  ## ``c`` the current module, a symbol table to a very good approximation
+  ## ``n`` the ast like it would be passed to a real macro
+  ## ``flags`` Some flags for more contextual information on how the
+  ## "macro" is calld.
+
   case n[0].sym.magic
   of mAddr:
     checkSonsLen(n, 2, c.config)
@@ -314,8 +322,53 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
   of mTypeOf:
     checkSonsLen(n, 2, c.config)
     result = semTypeOf(c, n.sons[1])
-  of mArrGet: result = semArrGet(c, n, flags)
-  of mArrPut: result = semArrPut(c, n, flags)
+  of mSizeOf:
+      # TODO there is no proper way to find out if a type cannot be queried for the size.
+      let size = getSize(c.config, n[1].typ)
+      # We just assume here that the type might come from the c backend
+      if size == szUnknownSize:
+        # Forward to the c code generation to emit a `sizeof` in the C code.
+        result = n
+      elif size >= 0:
+        result = newIntNode(nkIntLit, size)
+        result.info = n.info
+        result.typ = n.typ
+      else:
+
+        localError(c.config, n.info, "cannot evaluate 'sizeof' because its type is not defined completely")
+
+        result = nil
+
+
+  of mAlignOf:
+    result = newIntNode(nkIntLit, getAlign(c.config, n[1].typ))
+    result.info = n.info
+    result.typ = n.typ
+  of mOffsetOf:
+    var dotExpr: PNode
+
+    block findDotExpr:
+      if n[1].kind == nkDotExpr:
+        dotExpr = n[1]
+      elif n[1].kind == nkCheckedFieldExpr:
+        dotExpr = n[1][0]
+      else:
+        illFormedAst(n, c.config)
+
+    assert dotExpr != nil
+
+    let value = dotExpr[0]
+    let member = dotExpr[1]
+
+    discard computeSize(c.config, value.typ)
+
+    result = newIntNode(nkIntLit, member.sym.offset)
+    result.info = n.info
+    result.typ = n.typ
+  of mArrGet:
+    result = semArrGet(c, n, flags)
+  of mArrPut:
+    result = semArrPut(c, n, flags)
   of mAsgn:
     if n[0].sym.name.s == "=":
       result = semAsgnOpr(c, n)
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index f35a7d5cd..bbd966b38 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -1848,4 +1848,3 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
       s.position = result.len
       addSon(result, newSymNode(s))
       if sfGenSym notin s.flags: addDecl(c, s)
-
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 68d992791..2fc98c69b 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -2612,4 +2612,3 @@ tests:
 
     yes int, ordinal
     no  string, ordinal
-
diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim
new file mode 100644
index 000000000..98fd63e4a
--- /dev/null
+++ b/compiler/sizealignoffsetimpl.nim
@@ -0,0 +1,447 @@
+
+proc align(address, alignment: BiggestInt): BiggestInt =
+  result = (address + (alignment - 1)) and not (alignment - 1)
+
+const
+  ## a size is concidered "unknown" when it is an imported type from C
+  ## or C++.
+  szUnknownSize*      = -3
+  szIllegalRecursion* = -2
+  szUncomputedSize*   = -1
+
+proc computeSizeAlign(conf: ConfigRef; typ: PType): void
+
+proc computeSubObjectAlign(conf: ConfigRef; n: PNode): BiggestInt =
+  ## returns object alignment
+  case n.kind
+  of nkRecCase:
+    assert(n.sons[0].kind == nkSym)
+    result = computeSubObjectAlign(conf, n.sons[0])
+
+    for i in 1 ..< sonsLen(n):
+      let child = n.sons[i]
+      case child.kind
+      of nkOfBranch, nkElse:
+        let align = computeSubObjectAlign(conf, child.lastSon)
+        if align < 0:
+          return align
+        result = max(result, align)
+      else:
+        internalError(conf, "computeSubObjectAlign")
+
+  of nkRecList:
+    result = 1
+
+    for i, child in n.sons:
+      let align = computeSubObjectAlign(conf, n.sons[i])
+      if align < 0:
+        return align
+
+      result = max(result, align)
+
+  of nkSym:
+    computeSizeAlign(conf, n.sym.typ)
+    result = n.sym.typ.align
+
+  else:
+    result = 1
+
+proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOffset: BiggestInt): tuple[offset, align: BiggestInt] =
+  ## ``offset`` is the offset within the object, after the node has been written, no padding bytes added
+  ## ``align`` maximum alignment from all sub nodes
+
+  if n.typ != nil and n.typ.size == szIllegalRecursion:
+    result.offset = szIllegalRecursion
+    result.align  = szIllegalRecursion
+    return
+
+  result.align = 1
+  case n.kind
+  of nkRecCase:
+
+    assert(n.sons[0].kind == nkSym)
+    let (kindOffset, kindAlign) = computeObjectOffsetsFoldFunction(conf, n.sons[0], initialOffset)
+
+    var maxChildAlign: BiggestInt = 0
+
+    for i in 1 ..< sonsLen(n):
+      let child = n.sons[i]
+      case child.kind
+      of nkOfBranch, nkElse:
+        # offset parameter cannot be known yet, it needs to know the alignment first
+        let align = computeSubObjectAlign(conf, n.sons[i].lastSon)
+
+        if align == szIllegalRecursion:
+          result.offset  = szIllegalRecursion
+          result.align = szIllegalRecursion
+          return
+
+        if align == szUnknownSize or maxChildAlign == szUnknownSize:
+          maxChildAlign = szUnknownSize
+        else:
+          maxChildAlign = max(maxChildAlign, align)
+      else:
+        internalError(conf, "computeObjectOffsetsFoldFunction(record case branch)")
+
+    if maxChildAlign == szUnknownSize:
+      result.align  = szUnknownSize
+      result.offset = szUnknownSize
+
+    else:
+      # the union neds to be aligned first, before the offsets can be assigned
+      let kindUnionOffset = align(kindOffset, maxChildAlign)
+
+      var maxChildOffset: BiggestInt = 0
+      for i in 1 ..< sonsLen(n):
+        let (offset, align) = computeObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, kindUnionOffset)
+        maxChildOffset = max(maxChildOffset, offset)
+
+      result.align = max(kindAlign, maxChildAlign)
+      result.offset  = maxChildOffset
+
+
+  of nkRecList:
+
+    result.align = 1 # maximum of all member alignments
+
+    var offset = initialOffset
+
+    for i, child in n.sons:
+      let (new_offset, align) = computeObjectOffsetsFoldFunction(conf, child, offset)
+
+      if new_offset == szIllegalRecursion:
+        result.offset  = szIllegalRecursion
+        result.align = szIllegalRecursion
+        return
+
+      elif new_offset == szUnknownSize or offset == szUnknownSize:
+        # if anything is unknown, the rest becomes unknown as well
+        offset = szUnknownSize
+        result.align  = szUnknownSize
+
+      else:
+        offset = new_offset
+        result.align = max(result.align, align)
+
+    # final alignment
+    if offset == szUnknownSize:
+      result.offset = szUnknownSize
+    else:
+      result.offset = align(offset, result.align)
+
+  of nkSym:
+    computeSizeAlign(conf, n.sym.typ)
+    let size  = n.sym.typ.size
+    let align = n.sym.typ.align
+    result.align  = align
+    if initialOffset == szUnknownSize:
+      n.sym.offset  = szUnknownSize
+      result.offset = szUnknownSize
+    else:
+      n.sym.offset = align(initialOffset, align).int
+      result.offset = n.sym.offset + n.sym.typ.size
+
+  else:
+    result.align  = szUnknownSize
+    result.offset = szUnknownSize
+
+
+var recDepth = 0
+proc computePackedObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOffset: BiggestInt, debug : bool): BiggestInt =
+  ## ``result`` is the offset within the object, after the node has been written, no padding bytes added
+  recDepth += 1
+  defer:
+    recDepth -= 1
+
+  if debug:
+    if n.kind == nkSym:
+      echo repeat("--", recDepth) & "> ", initialOffset, "  ", n.kind, "  ", n.sym.name.s
+    else:
+      echo repeat("--", recDepth) & "> ", initialOffset, "  ", n.kind
+
+  case n.kind
+  of nkRecCase:
+
+    assert(n.sons[0].kind == nkSym)
+    let kindOffset = computePackedObjectOffsetsFoldFunction(conf, n.sons[0], initialOffset, debug)
+    # the union neds to be aligned first, before the offsets can be assigned
+    let kindUnionOffset = kindOffset
+
+    var maxChildOffset: BiggestInt = kindUnionOffset
+    for i in 1 ..< sonsLen(n):
+      let offset = computePackedObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, kindUnionOffset, debug)
+      maxChildOffset = max(maxChildOffset, offset)
+
+    if debug:
+      echo repeat("  ", recDepth), "result: ", maxChildOffset
+
+    result  = maxChildOffset
+
+  of nkRecList:
+    result = initialOffset
+    for i, child in n.sons:
+      result = computePackedObjectOffsetsFoldFunction(conf, child, result, debug)
+      if result == szIllegalRecursion:
+        break
+
+  of nkSym:
+    computeSizeAlign(conf, n.sym.typ)
+    n.sym.offset = initialOffset.int
+    result = n.sym.offset + n.sym.typ.size
+
+  else:
+    result = szUnknownSize
+
+# TODO this one needs an alignment map of the individual types
+
+proc computeSizeAlign(conf: ConfigRef; typ: PType) =
+  ## computes and sets ``size`` and ``align`` members of ``typ``
+
+  let hasSize  = typ.size  != szUncomputedSize
+  let hasAlign = typ.align != szUncomputedSize
+
+  if hasSize and hasAlign:
+    # nothing to do, size and align already computed
+    return
+
+  # This function can only calculate both, size and align at the same time.
+  # If one of them is already set this value is stored here and reapplied
+  let revertSize = typ.size
+  let revertAlign = typ.align
+  defer:
+    if hasSize:
+      typ.size = revertSize
+    if hasAlign:
+      typ.align = revertAlign
+
+  if typ.size == szIllegalRecursion or typ.align == szIllegalRecursion:
+    # we are already computing the size of the type
+    # --> illegal recursion in type
+    return
+
+  # mark computation in progress
+  typ.size  = szIllegalRecursion
+  typ.align = szIllegalRecursion
+
+  var maxAlign, sizeAccum, length: BiggestInt
+
+  var tk = typ.kind
+  case tk
+  of tyProc:
+    if typ.callConv == ccClosure:
+      typ.size = 2 * conf.target.ptrSize
+    else:
+      typ.size = conf.target.ptrSize
+    typ.align = int16(conf.target.ptrSize)
+
+  of tyNil:
+    typ.size = conf.target.ptrSize
+    typ.align = int16(conf.target.ptrSize)
+
+  of tyString:
+    if tfHasAsgn in typ.flags:
+      typ.size = conf.target.ptrSize * 2
+    else:
+      typ.size = conf.target.ptrSize
+    typ.align = int16(conf.target.ptrSize)
+
+  of tyCString, tySequence, tyPtr, tyRef, tyVar, tyLent, tyOpenArray:
+    let base = typ.lastSon
+    if base == typ:
+      # this is not the correct location to detect ``type A = ptr A``
+      typ.size  = szIllegalRecursion
+      typ.align = szIllegalRecursion
+      return
+
+    # recursive tuplers are not allowed and should be detected in the frontend
+    if base.kind == tyTuple:
+      computeSizeAlign(conf, base)
+      if base.size == szIllegalRecursion:
+        typ.size  = szIllegalRecursion
+        typ.align = szIllegalRecursion
+        return
+
+    typ.align = int16(conf.target.ptrSize)
+    if typ.kind == tySequence and tfHasAsgn in typ.flags:
+      typ.size  = conf.target.ptrSize * 2
+    else:
+      typ.size  = conf.target.ptrSize
+
+  of tyArray:
+    computeSizeAlign(conf, typ.sons[1])
+    let elemSize = typ.sons[1].size
+    if elemSize < 0:
+      typ.size  = elemSize
+      typ.align = int16(elemSize)
+    else:
+      typ.size  = lengthOrd(conf, typ.sons[0]) * elemSize
+      typ.align = typ.sons[1].align
+
+  of tyUncheckedArray:
+    let base = typ.lastSon
+    computeSizeAlign(conf, base)
+    # this should probably be szUnknownSize
+    typ.size = 0
+    typ.align = base.align
+  of tyEnum:
+    if firstOrd(conf, typ) < 0:
+      typ.size  = 4              # use signed int32
+      typ.align = 4
+    else:
+      length = lastOrd(conf, typ)   # BUGFIX: use lastOrd!
+      if length + 1 < `shl`(1, 8):
+        typ.size  = 1
+        typ.align = 1
+      elif length + 1 < `shl`(1, 16):
+        typ.size = 2
+        typ.align = 2
+      elif length + 1 < `shl`(BiggestInt(1), 32):
+        typ.size = 4
+        typ.align = 4
+      else:
+        typ.size = 8
+        typ.align = 8
+
+  of tySet:
+    if typ.sons[0].kind == tyGenericParam:
+      typ.size  = szUncomputedSize
+      typ.align = szUncomputedSize # in original version this was 1
+    else:
+      length = lengthOrd(conf, typ.sons[0])
+      if length <= 8:
+        typ.size = 1
+      elif length <= 16:
+        typ.size = 2
+      elif length <= 32:
+        typ.size = 4
+      elif length <= 64:
+        typ.size = 8
+      elif align(length, 8) mod 8 == 0:
+        typ.size  = align(length, 8) div 8
+      else:
+        typ.size = align(length, 8) div 8 + 1
+    typ.align = int16(typ.size)
+
+  of tyRange:
+    computeSizeAlign(conf, typ.sons[0])
+    typ.size = typ.sons[0].size
+    typ.align = typ.sons[0].align
+
+  of tyTuple:
+    maxAlign = 1
+    sizeAccum = 0
+
+    for i in countup(0, sonsLen(typ) - 1):
+      let child = typ.sons[i]
+      computeSizeAlign(conf, child)
+
+      if child.size == szIllegalRecursion:
+        typ.size  = szIllegalRecursion
+        typ.align = szIllegalRecursion
+        return
+
+      maxAlign = max(maxAlign, child.align)
+      sizeAccum = align(sizeAccum, child.align) + child.size
+
+    typ.size  = align(sizeAccum, maxAlign)
+    typ.align = int16(maxAlign)
+
+  of tyObject:
+    var headerSize : BiggestInt
+    var headerAlign: int16
+
+    if typ.sons[0] != nil:
+      # compute header size
+      var st = typ.sons[0]
+
+      while st.kind in skipPtrs:
+        st = st.sons[^1]
+
+      computeSizeAlign(conf, st)
+      if st.size == szIllegalRecursion:
+        typ.size = st.size
+        typ.align = st.align
+        return
+
+      headerSize  = st.size
+      headerAlign = st.align
+
+    elif isObjectWithTypeFieldPredicate(typ):
+      # this branch is taken for RootObj
+      headerSize = conf.target.intSize
+      headerAlign = conf.target.intSize.int16
+
+    else:
+      headerSize  = 0
+      headerAlign = 1
+
+    let (offset, align) =
+      if tfPacked in typ.flags:
+        (computePackedObjectOffsetsFoldFunction(conf, typ.n, headerSize, false), BiggestInt(1))
+      else:
+        computeObjectOffsetsFoldFunction(conf, typ.n, headerSize)
+
+    if offset == szIllegalRecursion:
+      typ.size = szIllegalRecursion
+      typ.align = szIllegalRecursion
+      return
+
+    if offset == szUnknownSize or (
+          typ.sym != nil and
+          typ.sym.flags * {sfCompilerProc, sfImportc} == {sfImportc}
+    ):
+      typ.size  = szUnknownSize
+      typ.align = szUnknownSize
+      return
+
+    # header size is already in size from computeObjectOffsetsFoldFunction
+    # maxAlign is probably not changed at all from headerAlign
+
+    if tfPacked in typ.flags:
+      typ.size = offset
+      typ.align = 1
+    else:
+      typ.align = int16(max(align, headerAlign))
+      typ.size  = align(offset, typ.align)
+
+  of tyInferred:
+    if typ.len > 1:
+      computeSizeAlign(conf, typ.lastSon)
+      typ.size = typ.lastSon.size
+      typ.align = typ.lastSon.align
+
+  of tyGenericInst, tyDistinct, tyGenericBody, tyAlias:
+    computeSizeAlign(conf, typ.lastSon)
+    typ.size = typ.lastSon.size
+    typ.align = typ.lastSon.align
+
+  of tyTypeClasses:
+    if typ.isResolvedUserTypeClass:
+      computeSizeAlign(conf, typ.lastSon)
+      typ.size = typ.lastSon.size
+      typ.align = typ.lastSon.align
+    else:
+      typ.size = szUncomputedSize
+      typ.align = szUncomputedSize
+
+  of tyTypeDesc:
+    computeSizeAlign(conf, typ.base)
+    typ.size = typ.base.size
+    typ.align = typ.base.align
+
+  of tyForward:
+    # is this really illegal recursion, or maybe just unknown?
+    typ.size = szIllegalRecursion
+    typ.align = szIllegalRecursion
+
+  of tyStatic:
+    if typ.n != nil:
+      computeSizeAlign(conf, typ.lastSon)
+      typ.size = typ.lastSon.size
+      typ.align = typ.lastSon.align
+    else:
+      typ.size = szUncomputedSize
+      typ.align = szUncomputedSize
+  else:
+    typ.size  = szUncomputedSize
+    typ.align = szUncomputedSize
diff --git a/compiler/types.nim b/compiler/types.nim
index e4e0e95ea..6f7d08d38 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -238,6 +238,7 @@ proc containsObject*(t: PType): bool =
   result = searchTypeFor(t, isObjectPredicate)
 
 proc isObjectWithTypeFieldPredicate(t: PType): bool =
+
   result = t.kind == tyObject and t.sons[0] == nil and
       not (t.sym != nil and {sfPure, sfInfixCall} * t.sym.flags != {}) and
       tfFinal notin t.flags
@@ -1142,7 +1143,6 @@ proc typeAllowedNode(marker: var IntSet, n: PNode, kind: TSymKind,
                      flags: TTypeAllowedFlags = {}): PType =
   if n != nil:
     result = typeAllowedAux(marker, n.typ, kind, flags)
-    #if not result: debug(n.typ)
     if result == nil:
       case n.kind
       of nkNone..nkNilLit:
@@ -1266,196 +1266,24 @@ proc typeAllowed*(t: PType, kind: TSymKind; flags: TTypeAllowedFlags = {}): PTyp
   var marker = initIntSet()
   result = typeAllowedAux(marker, t, kind, flags)
 
-proc align(address, alignment: BiggestInt): BiggestInt =
-  result = (address + (alignment - 1)) and not (alignment - 1)
-
-const
-  szNonConcreteType* = -3
-  szIllegalRecursion* = -2
-  szUnknownSize* = -1
-
-proc computeSizeAux(conf: ConfigRef; typ: PType, a: var BiggestInt): BiggestInt
-proc computeRecSizeAux(conf: ConfigRef; n: PNode, a, currOffset: var BiggestInt): BiggestInt =
-  var maxAlign, maxSize, b, res: BiggestInt
-  case n.kind
-  of nkRecCase:
-    assert(n.sons[0].kind == nkSym)
-    result = computeRecSizeAux(conf, n.sons[0], a, currOffset)
-    maxSize = 0
-    maxAlign = 1
-    for i in countup(1, sonsLen(n) - 1):
-      case n.sons[i].kind
-      of nkOfBranch, nkElse:
-        res = computeRecSizeAux(conf, lastSon(n.sons[i]), b, currOffset)
-        if res < 0: return res
-        maxSize = max(maxSize, res)
-        maxAlign = max(maxAlign, b)
-      else:
-        return szIllegalRecursion
-    currOffset = align(currOffset, maxAlign) + maxSize
-    result = align(result, maxAlign) + maxSize
-    a = maxAlign
-  of nkRecList:
-    result = 0
-    maxAlign = 1
-    for i in countup(0, sonsLen(n) - 1):
-      res = computeRecSizeAux(conf, n.sons[i], b, currOffset)
-      if res < 0: return res
-      currOffset = align(currOffset, b) + res
-      result = align(result, b) + res
-      if b > maxAlign: maxAlign = b
-    a = maxAlign
-  of nkSym:
-    result = computeSizeAux(conf, n.sym.typ, a)
-    n.sym.offset = int(currOffset)
-  else:
-    a = 1
-    result = szNonConcreteType
-
-proc computeSizeAux(conf: ConfigRef; typ: PType, a: var BiggestInt): BiggestInt =
-  var res, maxAlign, length, currOffset: BiggestInt
-  if typ.size == szIllegalRecursion:
-    # we are already computing the size of the type
-    # --> illegal recursion in type
-    return szIllegalRecursion
-  if typ.size >= 0:
-    # size already computed
-    result = typ.size
-    a = typ.align
-    return
-  typ.size = szIllegalRecursion # mark as being computed
-  case typ.kind
-  of tyInt, tyUInt:
-    result = conf.target.intSize
-    a = result
-  of tyInt8, tyUInt8, tyBool, tyChar:
-    result = 1
-    a = result
-  of tyInt16, tyUInt16:
-    result = 2
-    a = result
-  of tyInt32, tyUInt32, tyFloat32:
-    result = 4
-    a = result
-  of tyInt64, tyUInt64, tyFloat64:
-    result = 8
-    a = result
-  of tyFloat128:
-    result = 16
-    a = result
-  of tyFloat:
-    result = conf.target.floatSize
-    a = result
-  of tyProc:
-    if typ.callConv == ccClosure: result = 2 * conf.target.ptrSize
-    else: result = conf.target.ptrSize
-    a = conf.target.ptrSize
-  of tyString:
-    if tfHasAsgn in typ.flags:
-      result = conf.target.ptrSize * 2
-    else:
-      result = conf.target.ptrSize
-  of tyNil:
-    result = conf.target.ptrSize
-    a = result
-  of tyCString, tySequence, tyPtr, tyRef, tyVar, tyLent, tyOpenArray:
-    let base = typ.lastSon
-    if base == typ or (base.kind == tyTuple and base.size==szIllegalRecursion):
-      result = szIllegalRecursion
-    else:
-      if typ.kind == tySequence and tfHasAsgn in typ.flags:
-        result = conf.target.ptrSize * 2
-      else:
-        result = conf.target.ptrSize
-    a = result
-  of tyArray:
-    let elemSize = computeSizeAux(conf, typ.sons[1], a)
-    if elemSize < 0: return elemSize
-    result = lengthOrd(conf, typ.sons[0]) * elemSize
-  of tyEnum:
-    if firstOrd(conf, typ) < 0:
-      result = 4              # use signed int32
-    else:
-      length = lastOrd(conf, typ)   # BUGFIX: use lastOrd!
-      if length + 1 < `shl`(1, 8): result = 1
-      elif length + 1 < `shl`(1, 16): result = 2
-      elif length + 1 < `shl`(BiggestInt(1), 32): result = 4
-      else: result = 8
-    a = result
-  of tySet:
-    if typ.sons[0].kind == tyGenericParam:
-      result = szUnknownSize
-    else:
-      length = lengthOrd(conf, typ.sons[0])
-      if length <= 8: result = 1
-      elif length <= 16: result = 2
-      elif length <= 32: result = 4
-      elif length <= 64: result = 8
-      elif align(length, 8) mod 8 == 0: result = align(length, 8) div 8
-      else: result = align(length, 8) div 8 + 1
-    a = result
-  of tyRange:
-    result = computeSizeAux(conf, typ.sons[0], a)
-  of tyTuple:
-    result = 0
-    maxAlign = 1
-    for i in countup(0, sonsLen(typ) - 1):
-      res = computeSizeAux(conf, typ.sons[i], a)
-      if res < 0: return res
-      maxAlign = max(maxAlign, a)
-      result = align(result, a) + res
-    result = align(result, maxAlign)
-    a = maxAlign
-  of tyObject:
-    if typ.sons[0] != nil:
-      result = computeSizeAux(conf, typ.sons[0].skipTypes(skipPtrs), a)
-      if result < 0: return
-      maxAlign = a
-    elif isObjectWithTypeFieldPredicate(typ):
-      result = conf.target.intSize
-      maxAlign = result
-    else:
-      result = 0
-      maxAlign = 1
-    currOffset = result
-    result = computeRecSizeAux(conf, typ.n, a, currOffset)
-    if result < 0: return
-    if a < maxAlign: a = maxAlign
-    result = align(result, a)
-  of tyInferred:
-    if typ.len > 1:
-      result = computeSizeAux(conf, typ.lastSon, a)
-  of tyGenericInst, tyDistinct, tyGenericBody, tyAlias, tySink:
-    result = computeSizeAux(conf, lastSon(typ), a)
-  of tyTypeClasses:
-    result = if typ.isResolvedUserTypeClass: computeSizeAux(conf, typ.lastSon, a)
-             else: szUnknownSize
-  of tyTypeDesc:
-    result = computeSizeAux(conf, typ.base, a)
-  of tyForward: return szIllegalRecursion
-  of tyStatic:
-    result = if typ.n != nil: computeSizeAux(conf, typ.lastSon, a)
-             else: szUnknownSize
-  of tyUncheckedArray:
-    result = 0
-  else:
-    #internalError("computeSizeAux()")
-    result = szUnknownSize
-  typ.size = result
-  typ.align = int16(a)
+include sizealignoffsetimpl
 
 proc computeSize*(conf: ConfigRef; typ: PType): BiggestInt =
-  var a: BiggestInt = 1
-  result = computeSizeAux(conf, typ, a)
+  computeSizeAlign(conf, typ)
+  result = typ.size
 
 proc getReturnType*(s: PSym): PType =
   # Obtains the return type of a iterator/proc/macro/template
   assert s.kind in skProcKinds
   result = s.typ.sons[0]
 
+proc getAlign*(conf: ConfigRef; typ: PType): BiggestInt =
+  computeSizeAlign(conf, typ)
+  result = typ.align
+
 proc getSize*(conf: ConfigRef; typ: PType): BiggestInt =
-  result = computeSize(conf, typ)
-  if result < 0: internalError(conf, "getSize: " & $typ.kind)
+  computeSizeAlign(conf, typ)
+  result = typ.size
 
 proc containsGenericTypeIter(t: PType, closure: RootRef): bool =
   case t.kind
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index ca0b24ddc..14d411f9b 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -1080,8 +1080,6 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     c.gABC(n, if m == mOf: opcOf else: opcIs, dest, tmp, idx)
     c.freeTemp(tmp)
     c.freeTemp(idx)
-  of mSizeOf:
-    globalError(c.config, n.info, "cannot run in the VM: " & renderTree(n))
   of mHigh:
     if dest < 0: dest = c.getTemp(n.typ)
     let tmp = c.genx(n.sons[1])
@@ -1231,6 +1229,8 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
       # produces a value
     else:
       globalError(c.config, n.info, "expandToAst requires a call expression")
+  of mSizeOf, mAlignOf:
+    globalError(c.config, n.info, "cannot evaluate 'sizeof/alignof' because its type is not defined completely")
   of mRunnableExamples:
     discard "just ignore any call to runnableExamples"
   else:
diff --git a/lib/system.nim b/lib/system.nim
index 1f3000aa8..c1ea2e5c4 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -682,13 +682,30 @@ proc sizeof*[T](x: T): int {.magic: "SizeOf", noSideEffect.}
   ## that one never needs to know ``x``'s size. As a special semantic rule,
   ## ``x`` may also be a type identifier (``sizeof(int)`` is valid).
   ##
-  ## Limitations: If used within nim VM context ``sizeof`` will only work
-  ## for simple types.
+  ## Limitations: If used for types that are imported from C or C++,
+  ## sizeof should fallback to the ``sizeof`` in the C compiler. The
+  ## result isn't available for the Nim compiler and therefore can't
+  ## be used inside of macros.
   ##
   ## .. code-block:: nim
   ##  sizeof('A') #=> 1
   ##  sizeof(2) #=> 8
 
+when defined(nimHasalignOf):
+  proc alignof*[T](x: T): int {.magic: "AlignOf", noSideEffect.}
+  proc alignof*(x: typedesc): int {.magic: "AlignOf", noSideEffect.}
+
+  proc offsetOfDotExpr(typeAccess: typed): int {.magic: "OffsetOf", noSideEffect, compileTime.}
+
+  template offsetOf*[T](t: typedesc[T]; member: untyped): int =
+    var tmp: T
+    offsetOfDotExpr(tmp.member)
+
+  template offsetOf*[T](value: T; member: untyped): int =
+    offsetOfDotExpr(value.member)
+
+  #proc offsetOf*(memberaccess: typed): int {.magic: "OffsetOf", noSideEffect.}
+
 when defined(nimtypedescfixed):
   proc sizeof*(x: typedesc): int {.magic: "SizeOf", noSideEffect.}
 
diff --git a/testament/tester.nim b/testament/tester.nim
index 024d02ed4..cf649c0c0 100644
--- a/testament/tester.nim
+++ b/testament/tester.nim
@@ -467,7 +467,7 @@ proc main() =
     of "targets":
       targetsStr = p.val.string
       targets = parseTargets(targetsStr)
-    of "nim": compilerPrefix = p.val.string
+    of "nim": compilerPrefix = p.val.string & " "
     else: quit Usage
     p.next()
   if p.kind != cmdArgument: quit Usage
diff --git a/tests/misc/tsizeof.nim b/tests/misc/tsizeof.nim
index 0597535f9..ecdd44fca 100644
--- a/tests/misc/tsizeof.nim
+++ b/tests/misc/tsizeof.nim
@@ -1,14 +1,8 @@
 discard """
-  file: "tsize.nim"
-  output: "40 3 12 32"
+  output: "OK"
 """
-type
-  TMyRecord {.final.} = object
-    x, y: int
-    b: bool
-    r: float
-    s: string
 
+type
   TMyEnum = enum
     tmOne, tmTwo, tmThree, tmFour
 
@@ -16,13 +10,345 @@ type
   TMyArray2 = array[1..3, int32]
   TMyArray3 = array[TMyEnum, float64]
 
-const 
+const
   mysize1 = sizeof(TMyArray1)
   mysize2 = sizeof(TMyArray2)
   mysize3 = sizeof(TMyArray3)
 
-write(stdout, sizeof(TMyRecord))
-echo ' ', mysize1, ' ', mysize2, ' ',mysize3
+assert mysize1 == 3
+assert mysize2 == 12
+assert mysize3 == 32
+
+import macros, typetraits
+
+macro testSizeAlignOf(args: varargs[untyped]): untyped =
+  result = newStmtList()
+  for arg in args:
+    result.add quote do:
+      let
+        c_size = c_sizeof(`arg`)
+        nim_size = sizeof(`arg`)
+        c_align = c_alignof(type(`arg`))
+        nim_align = alignof(`arg`)
+
+      if nim_size != c_size or nim_align != c_align:
+        var msg = strAlign(`arg`.type.name & ": ")
+        if nim_size != c_size:
+          msg.add  " size(got, expected):  " & $nim_size  & " != " & $c_size
+        if nim_align != c_align:
+          msg.add  " align(get, expected): " & $nim_align & " != " & $c_align
+        echo msg
+
+
+macro testOffsetOf(a,b1,b2: untyped): untyped =
+  let typeName = newLit(a.repr)
+  let member   = newLit(b2.repr)
+  result = quote do:
+    let
+      c_offset   = c_offsetof(`a`,`b1`)
+      nim_offset = offsetof(`a`,`b2`)
+    if c_offset != nim_offset:
+      echo `typeName`, ".", `member`, " offset: ", c_offset, " != ", nim_offset
+
+template testOffsetOf(a,b: untyped): untyped =
+  testOffsetOf(a,b,b)
+
+proc strAlign(arg: string): string =
+  const minLen = 22
+  result = arg
+  for i in 0 ..< minLen - arg.len:
+    result &= ' '
+
+macro c_offsetof(a: typed, b: untyped): int32 =
+  ## Buffet proof implementation that works on actual offsetof operator
+  ## in the c backend. Assuming of course this implementation is
+  ## correct.
+  let bliteral =
+    if b.kind == nnkStrLit:
+      b
+    else:
+      newLit(repr(b))
+  result = quote do:
+    var res: int32
+    {.emit: [res, " = offsetof(", `a`, ", ", `bliteral`, ");"] .}
+    res
+
+macro c_sizeof(a: typed): int32 =
+  ## Buffet proof implementation that works using the sizeof operator
+  ## in the c backend. Assuming of course this implementation is
+  ## correct.
+  result = quote do:
+    var res: int32
+    {.emit: [res, " = sizeof(", `a`, ");"] .}
+    res
+
+macro c_alignof(arg: untyped): untyped =
+  ## Buffet proof implementation that works on actual alignment
+  ## behavior measured at runtime.
+  let typeSym = genSym(nskType, "AlignTestType"&arg.repr)
+  result = quote do:
+    type
+      `typeSym` = object
+        causeAlign: byte
+        member: `arg`
+    c_offsetof(`typeSym`, member)
+
+macro testAlign(arg:untyped):untyped =
+  let prefix = newLit(arg.lineinfo & "  alignof " & arg.repr & " ")
+  result = quote do:
+    let cAlign = c_alignof(`arg`)
+    let nimAlign = alignof(`arg`)
+    if cAlign != nimAlign:
+      echo `prefix`, cAlign, " != ", nimAlign
+
+testAlign(pointer)
+testAlign(int)
+testAlign(uint)
+testAlign(int8)
+testAlign(int16)
+testAlign(int32)
+testAlign(int64)
+testAlign(uint8)
+testAlign(uint16)
+testAlign(uint32)
+testAlign(uint64)
+testAlign(float)
+testAlign(float32)
+testAlign(float64)
+
+type
+  MyEnum {.pure.} = enum
+    ValueA
+    ValueB
+    ValueC
+
+  OtherEnum {.pure, size: 8.} = enum
+    ValueA
+    ValueB
+
+  Enum1 {.pure, size: 1.} = enum
+    ValueA
+    ValueB
+
+  Enum2 {.pure, size: 2.} = enum
+    ValueA
+    ValueB
+
+  Enum4 {.pure, size: 4.} = enum
+    ValueA
+    ValueB
+
+  Enum8 {.pure, size: 8.} = enum
+    ValueA
+    ValueB
+
+testAlign(MyEnum)
+testAlign(OtherEnum)
+testAlign(Enum1)
+testAlign(Enum2)
+testAlign(Enum4)
+testAlign(Enum8)
+
+
+template testinstance(body: untyped): untyped =
+  block:
+    {.pragma: objectconfig.}
+    body
+
+  block:
+    {.pragma: objectconfig, packed.}
+    body
+
+testinstance:
+  type
+
+    EnumObjectA  {.objectconfig.} = object
+      a : Enum1
+      b : Enum2
+      c : Enum4
+      d : Enum8
+
+    EnumObjectB  {.objectconfig.} = object
+      a : Enum8
+      b : Enum4
+      c : Enum2
+      d : Enum1
+
+    TrivialType  {.objectconfig.} = object
+      x,y,z: int8
+
+    SimpleAlignment {.objectconfig.} = object
+      # behaves differently on 32bit Windows and 32bit Linux
+      a,b: int8
+      c: int64
+
+    AlignAtEnd {.objectconfig.} = object
+      a: int64
+      b,c: int8
+
+    SimpleBranch {.objectconfig.} = object
+      case kind: MyEnum
+      of MyEnum.ValueA:
+        a: int16
+      of MyEnum.ValueB:
+        b: int32
+      of MyEnum.ValueC:
+        c: int64
+
+    PaddingBeforeBranchA {.objectconfig.} = object
+      cause: int8
+      case kind: MyEnum
+      of MyEnum.ValueA:
+        a: int16
+      of MyEnum.ValueB:
+        b: int32
+      of MyEnum.ValueC:
+        c: int64
+
+    PaddingBeforeBranchB {.objectconfig.} = object
+      cause: int8
+      case kind: MyEnum
+      of MyEnum.ValueA:
+        a: int8
+      of MyEnum.ValueB:
+        b: int16
+      of MyEnum.ValueC:
+        c: int32
+
+    PaddingAfterBranch {.objectconfig.} = object
+      case kind: MyEnum
+      of MyEnum.ValueA:
+        a: int8
+      of MyEnum.ValueB:
+        b: int16
+      of MyEnum.ValueC:
+        c: int32
+      cause: int64
+
+    RecursiveStuff {.objectconfig.} = object
+      case kind: MyEnum    # packedOffset:    0
+      of MyEnum.ValueA:    # packedOffset:
+        a: int16           # packedOffset:    1
+      of MyEnum.ValueB:    # packedOffset:
+        b: int32           # packedOffset:    1
+      of MyEnum.ValueC:    # packedOffset:
+        case kind2: MyEnum # packedOffset:    1
+        of MyEnum.ValueA:  # packedOffset:
+          ca1: int8
+          ca2: int32
+        of MyEnum.ValueB:  # packedOffset:
+          cb: int32        # packedOffset:    2
+        of MyEnum.ValueC:  # packedOffset:
+          cc: int64        # packedOffset:    2
+        d1: int8
+        d2: int64
+
+    Foobar {.objectconfig.} = object
+      case kind: OtherEnum
+      of OtherEnum.ValueA:
+        a: uint8
+      of OtherEnum.ValueB:
+        b: int8
+      c: int8
+
+    Bazing {.objectconfig.} = object of RootObj
+      a: int64
+      # TODO test on 32 bit system
+      # only there the object header is smaller than the first member
+
+    InheritanceA {.objectconfig.} = object of RootObj
+      a: char
+
+    InheritanceB {.objectconfig.} = object of InheritanceA
+      b: char
+
+    InheritanceC {.objectconfig.} = object of InheritanceB
+      c: char
+
+    #Float128Test = object
+    #  a: byte
+    #  b: float128
+
+    #Bazang = object of RootObj
+    #  a: float128
+
+  const trivialSize = sizeof(TrivialType) # needs to be able to evaluate at compile time
+
+  testAlign(SimpleAlignment)
+
+  proc main(): void =
+    var t : TrivialType
+    var a : SimpleAlignment
+    var b : AlignAtEnd
+    var c : SimpleBranch
+    var d : PaddingBeforeBranchA
+    var e : PaddingBeforeBranchB
+    var f : PaddingAfterBranch
+    var g : RecursiveStuff
+    var ro : RootObj
+    var
+      e1: Enum1
+      e2: Enum2
+      e4: Enum4
+      e8: Enum8
+    var
+      eoa: EnumObjectA
+      eob: EnumObjectB
+
+    testSizeAlignOf(t,a,b,c,d,e,f,g,ro, e1, e2, e4, e8, eoa, eob)
+
+    testOffsetOf(TrivialType, x)
+    testOffsetOf(TrivialType, y)
+    testOffsetOf(TrivialType, z)
+
+    testOffsetOf(SimpleAlignment, a)
+    testOffsetOf(SimpleAlignment, b)
+    testOffsetOf(SimpleAlignment, c)
+    testOffsetOf(AlignAtEnd, a)
+    testOffsetOf(AlignAtEnd, b)
+    testOffsetOf(AlignAtEnd, c)
+
+    testOffsetOf(SimpleBranch, "_Ukind", a)
+    testOffsetOf(SimpleBranch, "_Ukind", b)
+    testOffsetOf(SimpleBranch, "_Ukind", c)
+
+    testOffsetOf(PaddingBeforeBranchA, cause)
+    testOffsetOf(PaddingBeforeBranchA, "_Ukind", a)
+    testOffsetOf(PaddingBeforeBranchB, cause)
+    testOffsetOf(PaddingBeforeBranchB, "_Ukind", a)
+
+    testOffsetOf(PaddingAfterBranch, "_Ukind", a)
+    testOffsetOf(PaddingAfterBranch, cause)
+
+    testOffsetOf(Foobar, c)
+
+    testOffsetOf(Bazing, a)
+
+    testOffsetOf(InheritanceA, a)
+    testOffsetOf(InheritanceB, b)
+    testOffsetOf(InheritanceC, c)
+
+    testOffsetOf(EnumObjectA, a)
+    testOffsetOf(EnumObjectA, b)
+    testOffsetOf(EnumObjectA, c)
+    testOffsetOf(EnumObjectA, d)
+    testOffsetOf(EnumObjectB, a)
+    testOffsetOf(EnumObjectB, b)
+    testOffsetOf(EnumObjectB, c)
+    testOffsetOf(EnumObjectB, d)
+
+    testOffsetOf(RecursiveStuff, kind)
+    testOffsetOf(RecursiveStuff, "_Ukind.S1.a",              a)
+    testOffsetOf(RecursiveStuff, "_Ukind.S2.b",              b)
+    testOffsetOf(RecursiveStuff, "_Ukind.S3.kind2",          kind2)
+    testOffsetOf(RecursiveStuff, "_Ukind.S3._Ukind2.S1.ca1", ca1)
+    testOffsetOf(RecursiveStuff, "_Ukind.S3._Ukind2.S1.ca2", ca2)
+    testOffsetOf(RecursiveStuff, "_Ukind.S3._Ukind2.S2.cb",  cb)
+    testOffsetOf(RecursiveStuff, "_Ukind.S3._Ukind2.S3.cc",  cc)
+    testOffsetOf(RecursiveStuff, "_Ukind.S3.d1",             d1)
+    testOffsetOf(RecursiveStuff, "_Ukind.S3.d2",             d2)
 
+  main()
 
 
+echo "OK"
diff --git a/tests/misc/tsizeof2.nim b/tests/misc/tsizeof2.nim
new file mode 100644
index 000000000..67379871d
--- /dev/null
+++ b/tests/misc/tsizeof2.nim
@@ -0,0 +1,11 @@
+discard """
+errormsg: "cannot evaluate 'sizeof/alignof' because its type is not defined completely"
+line: 9
+"""
+
+type
+  MyStruct {.importc: "MyStruct".} = object
+
+const i = sizeof(MyStruct)
+
+echo i
diff --git a/tests/types/tillegalrecursion3.nim b/tests/types/tillegalrecursion3.nim
new file mode 100644
index 000000000..fb5c1641c
--- /dev/null
+++ b/tests/types/tillegalrecursion3.nim
@@ -0,0 +1,12 @@
+discard """
+  errormsg: "illegal recursion in type 'Foo'"
+"""
+
+type
+  Imported {.importc.} = object
+
+  Foo = object
+    b: Imported
+    a: Foo
+
+var myFoo: Foo