implemented 'union' and 'unchecked' pragmas
This commit is contained in:
parent
a066e4e5c5
commit
5506e8491d
9 changed files with 94 additions and 25 deletions
|
|
@ -488,6 +488,8 @@ const
|
||||||
routineKinds* = {skProc, skMethod, skIterator, skConverter,
|
routineKinds* = {skProc, skMethod, skIterator, skConverter,
|
||||||
skMacro, skTemplate}
|
skMacro, skTemplate}
|
||||||
tfIncompleteStruct* = tfVarargs
|
tfIncompleteStruct* = tfVarargs
|
||||||
|
tfUncheckedArray* = tfVarargs
|
||||||
|
tfUnion* = tfNoSideEffect
|
||||||
skError* = skUnknown
|
skError* = skUnknown
|
||||||
|
|
||||||
# type flags that are essential for type equality:
|
# type flags that are essential for type equality:
|
||||||
|
|
|
||||||
|
|
@ -736,7 +736,7 @@ proc genArrayElem(p: BProc, e: PNode, d: var TLoc) =
|
||||||
var ty = skipTypes(skipTypes(a.t, abstractVarRange), abstractPtrs)
|
var ty = skipTypes(skipTypes(a.t, abstractVarRange), abstractPtrs)
|
||||||
var first = intLiteral(firstOrd(ty))
|
var first = intLiteral(firstOrd(ty))
|
||||||
# emit range check:
|
# emit range check:
|
||||||
if (optBoundsCheck in p.options):
|
if optBoundsCheck in p.options and tfUncheckedArray notin ty.flags:
|
||||||
if not isConstExpr(e.sons[1]):
|
if not isConstExpr(e.sons[1]):
|
||||||
# semantic pass has already checked for const index expressions
|
# semantic pass has already checked for const index expressions
|
||||||
if firstOrd(ty) == 0:
|
if firstOrd(ty) == 0:
|
||||||
|
|
|
||||||
|
|
@ -376,10 +376,13 @@ proc getTypePre(m: BModule, typ: PType): PRope =
|
||||||
else:
|
else:
|
||||||
result = getSimpleTypeDesc(m, typ)
|
result = getSimpleTypeDesc(m, typ)
|
||||||
if result == nil: result = cacheGetType(m.typeCache, typ)
|
if result == nil: result = cacheGetType(m.typeCache, typ)
|
||||||
|
|
||||||
|
proc structOrUnion(t: PType): PRope =
|
||||||
|
(if tfUnion in t.flags: toRope("union") else: toRope("struct"))
|
||||||
|
|
||||||
proc getForwardStructFormat(): string =
|
proc getForwardStructFormat(): string =
|
||||||
if gCmd == cmdCompileToCpp: result = "struct $1;$n"
|
if gCmd == cmdCompileToCpp: result = "$1 $2;$n"
|
||||||
else: result = "typedef struct $1 $1;$n"
|
else: result = "typedef $1 $2 $2;$n"
|
||||||
|
|
||||||
proc getTypeForward(m: BModule, typ: PType): PRope =
|
proc getTypeForward(m: BModule, typ: PType): PRope =
|
||||||
result = cacheGetType(m.forwTypeCache, typ)
|
result = cacheGetType(m.forwTypeCache, typ)
|
||||||
|
|
@ -390,7 +393,8 @@ proc getTypeForward(m: BModule, typ: PType): PRope =
|
||||||
of tySequence, tyTuple, tyObject:
|
of tySequence, tyTuple, tyObject:
|
||||||
result = getTypeName(typ)
|
result = getTypeName(typ)
|
||||||
if not isImportedType(typ):
|
if not isImportedType(typ):
|
||||||
appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result])
|
appf(m.s[cfsForwardTypes], getForwardStructFormat(),
|
||||||
|
[structOrUnion(typ), result])
|
||||||
idTablePut(m.forwTypeCache, typ, result)
|
idTablePut(m.forwTypeCache, typ, result)
|
||||||
else: internalError("getTypeForward(" & $typ.kind & ')')
|
else: internalError("getTypeForward(" & $typ.kind & ')')
|
||||||
|
|
||||||
|
|
@ -445,7 +449,12 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
|
||||||
if accessExpr != nil: ae = ropef("$1.$2", [accessExpr, sname])
|
if accessExpr != nil: ae = ropef("$1.$2", [accessExpr, sname])
|
||||||
else: ae = sname
|
else: ae = sname
|
||||||
fillLoc(field.loc, locField, field.typ, ae, OnUnknown)
|
fillLoc(field.loc, locField, field.typ, ae, OnUnknown)
|
||||||
appf(result, "$1 $2;$n", [getTypeDescAux(m, field.loc.t, check), sname])
|
let fieldType = field.loc.t
|
||||||
|
if fieldType.kind == tyArray and tfUncheckedArray in fieldType.flags:
|
||||||
|
appf(result, "$1 $2[SEQ_DECL_SIZE];$n",
|
||||||
|
[getTypeDescAux(m, fieldType.elemType, check), sname])
|
||||||
|
else:
|
||||||
|
appf(result, "$1 $2;$n", [getTypeDescAux(m, fieldType, check), sname])
|
||||||
else: internalError(n.info, "genRecordFieldsAux()")
|
else: internalError(n.info, "genRecordFieldsAux()")
|
||||||
|
|
||||||
proc getRecordFields(m: BModule, typ: PType, check: var TIntSet): PRope =
|
proc getRecordFields(m: BModule, typ: PType, check: var TIntSet): PRope =
|
||||||
|
|
@ -455,23 +464,24 @@ proc getRecordDesc(m: BModule, typ: PType, name: PRope,
|
||||||
check: var TIntSet): PRope =
|
check: var TIntSet): PRope =
|
||||||
# declare the record:
|
# declare the record:
|
||||||
var hasField = false
|
var hasField = false
|
||||||
|
let aStruct = structOrUnion(typ)
|
||||||
if typ.kind == tyObject:
|
if typ.kind == tyObject:
|
||||||
if typ.sons[0] == nil:
|
if typ.sons[0] == nil:
|
||||||
if (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags:
|
if (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags:
|
||||||
result = ropecg(m, "struct $1 {$n", [name])
|
result = ropecg(m, "$1 $2 {$n", [aStruct, name])
|
||||||
else:
|
else:
|
||||||
result = ropecg(m, "struct $1 {$n#TNimType* m_type;$n", [name])
|
result = ropecg(m, "$1 $2 {$n#TNimType* m_type;$n", [aStruct, name])
|
||||||
hasField = true
|
hasField = true
|
||||||
elif gCmd == cmdCompileToCpp:
|
elif gCmd == cmdCompileToCpp:
|
||||||
result = ropecg(m, "struct $1 : public $2 {$n",
|
result = ropecg(m, "$1 $2 : public $3 {$n",
|
||||||
[name, getTypeDescAux(m, typ.sons[0], check)])
|
[aStruct, name, getTypeDescAux(m, typ.sons[0], check)])
|
||||||
hasField = true
|
hasField = true
|
||||||
else:
|
else:
|
||||||
result = ropecg(m, "struct $1 {$n $2 Sup;$n",
|
result = ropecg(m, "$1 $2 {$n $3 Sup;$n",
|
||||||
[name, getTypeDescAux(m, typ.sons[0], check)])
|
[aStruct, name, getTypeDescAux(m, typ.sons[0], check)])
|
||||||
hasField = true
|
hasField = true
|
||||||
else:
|
else:
|
||||||
result = ropef("struct $1 {$n", [name])
|
result = ropef("$1 $2 {$n", [aStruct, name])
|
||||||
var desc = getRecordFields(m, typ, check)
|
var desc = getRecordFields(m, typ, check)
|
||||||
if (desc == nil) and not hasField:
|
if (desc == nil) and not hasField:
|
||||||
appf(result, "char dummy;$n", [])
|
appf(result, "char dummy;$n", [])
|
||||||
|
|
@ -480,8 +490,8 @@ proc getRecordDesc(m: BModule, typ: PType, name: PRope,
|
||||||
app(result, "};" & tnl)
|
app(result, "};" & tnl)
|
||||||
|
|
||||||
proc getTupleDesc(m: BModule, typ: PType, name: PRope,
|
proc getTupleDesc(m: BModule, typ: PType, name: PRope,
|
||||||
check: var TIntSet): PRope =
|
check: var TIntSet): PRope =
|
||||||
result = ropef("struct $1 {$n", [name])
|
result = ropef("$1 $2 {$n", [structOrUnion(typ), name])
|
||||||
var desc: PRope = nil
|
var desc: PRope = nil
|
||||||
for i in countup(0, sonsLen(typ) - 1):
|
for i in countup(0, sonsLen(typ) - 1):
|
||||||
appf(desc, "$1 Field$2;$n",
|
appf(desc, "$1 Field$2;$n",
|
||||||
|
|
@ -557,7 +567,8 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope =
|
||||||
if result == nil:
|
if result == nil:
|
||||||
result = getTypeName(t)
|
result = getTypeName(t)
|
||||||
if not isImportedType(t):
|
if not isImportedType(t):
|
||||||
appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result])
|
appf(m.s[cfsForwardTypes], getForwardStructFormat(),
|
||||||
|
[structOrUnion(t), result])
|
||||||
idTablePut(m.forwTypeCache, t, result)
|
idTablePut(m.forwTypeCache, t, result)
|
||||||
assert(cacheGetType(m.typeCache, t) == nil)
|
assert(cacheGetType(m.typeCache, t) == nil)
|
||||||
idTablePut(m.typeCache, t, con(result, "*"))
|
idTablePut(m.typeCache, t, con(result, "*"))
|
||||||
|
|
@ -588,7 +599,8 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope =
|
||||||
if result == nil:
|
if result == nil:
|
||||||
result = getTypeName(t)
|
result = getTypeName(t)
|
||||||
if not isImportedType(t):
|
if not isImportedType(t):
|
||||||
appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result])
|
appf(m.s[cfsForwardTypes], getForwardStructFormat(),
|
||||||
|
[structOrUnion(t), result])
|
||||||
idTablePut(m.forwTypeCache, t, result)
|
idTablePut(m.forwTypeCache, t, result)
|
||||||
idTablePut(m.typeCache, t, result) # always call for sideeffects:
|
idTablePut(m.typeCache, t, result) # always call for sideeffects:
|
||||||
if t.kind != tyTuple: recdesc = getRecordDesc(m, t, result, check)
|
if t.kind != tyTuple: recdesc = getRecordDesc(m, t, result, check)
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ proc initDefines*() =
|
||||||
defineSymbol("nimeffects")
|
defineSymbol("nimeffects")
|
||||||
defineSymbol("nimbabel")
|
defineSymbol("nimbabel")
|
||||||
defineSymbol("nimcomputedgoto")
|
defineSymbol("nimcomputedgoto")
|
||||||
|
defineSymbol("nimunion")
|
||||||
|
|
||||||
# add platform specific symbols:
|
# add platform specific symbols:
|
||||||
case targetCPU
|
case targetCPU
|
||||||
|
|
|
||||||
|
|
@ -50,9 +50,9 @@ const
|
||||||
wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame,
|
wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame,
|
||||||
wRaises, wTags}
|
wRaises, wTags}
|
||||||
typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
|
typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
|
||||||
wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow,
|
wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow,
|
||||||
wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef,
|
wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef,
|
||||||
wInheritable, wGensym, wInject, wRequiresInit}
|
wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion}
|
||||||
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
|
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
|
||||||
wImportCpp, wImportObjC, wError}
|
wImportCpp, wImportObjC, wError}
|
||||||
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
|
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
|
||||||
|
|
@ -718,6 +718,14 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||||
noVal(it)
|
noVal(it)
|
||||||
if sym.typ == nil: invalidPragma(it)
|
if sym.typ == nil: invalidPragma(it)
|
||||||
else: incl(sym.typ.flags, tfIncompleteStruct)
|
else: incl(sym.typ.flags, tfIncompleteStruct)
|
||||||
|
of wUnchecked:
|
||||||
|
noVal(it)
|
||||||
|
if sym.typ == nil: invalidPragma(it)
|
||||||
|
else: incl(sym.typ.flags, tfUncheckedArray)
|
||||||
|
of wUnion:
|
||||||
|
noVal(it)
|
||||||
|
if sym.typ == nil: invalidPragma(it)
|
||||||
|
else: incl(sym.typ.flags, tfUnion)
|
||||||
of wRequiresInit:
|
of wRequiresInit:
|
||||||
noVal(it)
|
noVal(it)
|
||||||
if sym.typ == nil: invalidPragma(it)
|
if sym.typ == nil: invalidPragma(it)
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ type
|
||||||
wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto, wInjectStmt,
|
wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto, wInjectStmt,
|
||||||
wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit,
|
wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit,
|
||||||
wAsmNoStackFrame,
|
wAsmNoStackFrame,
|
||||||
wImplicitStatic, wGlobal, wCodegenDecl,
|
wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked,
|
||||||
|
|
||||||
wAuto, wBool, wCatch, wChar, wClass,
|
wAuto, wBool, wCatch, wChar, wClass,
|
||||||
wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast,
|
wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast,
|
||||||
|
|
@ -145,7 +145,7 @@ const
|
||||||
"subschar", "acyclic", "shallow", "unroll", "linearscanend",
|
"subschar", "acyclic", "shallow", "unroll", "linearscanend",
|
||||||
"computedgoto", "injectstmt",
|
"computedgoto", "injectstmt",
|
||||||
"write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit",
|
"write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit",
|
||||||
"asmnostackframe", "implicitstatic", "global", "codegendecl",
|
"asmnostackframe", "implicitstatic", "global", "codegendecl", "unchecked",
|
||||||
|
|
||||||
"auto", "bool", "catch", "char", "class",
|
"auto", "bool", "catch", "char", "class",
|
||||||
"const_cast", "default", "delete", "double",
|
"const_cast", "default", "delete", "double",
|
||||||
|
|
|
||||||
|
|
@ -5320,6 +5320,53 @@ strings automatically:
|
||||||
printf("hallo %s", "world") # "world" will be passed as C string
|
printf("hallo %s", "world") # "world" will be passed as C string
|
||||||
|
|
||||||
|
|
||||||
|
Union pragma
|
||||||
|
------------
|
||||||
|
The `union`:idx: pragma can be applied to any ``object`` type. It means all
|
||||||
|
of the object's fields are overlaid in memory. This produces a ``union``
|
||||||
|
instead of a ``struct`` in the generated C/C++ code. The object declaration
|
||||||
|
then must not use inheritance or any GC'ed memory but this is currently not
|
||||||
|
checked.
|
||||||
|
|
||||||
|
**Future directions**: GC'ed memory should be allowed in unions and the GC
|
||||||
|
should scan unions conservatively.
|
||||||
|
|
||||||
|
|
||||||
|
Unchecked pragma
|
||||||
|
----------------
|
||||||
|
The `unchecked`:idx: pragma can be used to mark a named array as ``unchecked``
|
||||||
|
meaning its bounds are not checked. This is often useful when one wishes to
|
||||||
|
implement his own flexibly sized arrays. Additionally an unchecked array is
|
||||||
|
translated into a C array of undetermined size:
|
||||||
|
|
||||||
|
.. code-block:: nimrod
|
||||||
|
type
|
||||||
|
ArrayPart{.unchecked.} = array[0..0, int]
|
||||||
|
MySeq = object
|
||||||
|
len, cap: int
|
||||||
|
data: ArrayPart
|
||||||
|
|
||||||
|
Produces roughly this C code:
|
||||||
|
|
||||||
|
.. code-block:: C
|
||||||
|
typedef struct {
|
||||||
|
NI len;
|
||||||
|
NI cap;
|
||||||
|
NI data[];
|
||||||
|
} MySeq;
|
||||||
|
|
||||||
|
The bounds checking done at compile time is not disabled for now, so to access
|
||||||
|
``s.data[C]`` (where ``C`` is a constant) the array's index needs needs to
|
||||||
|
include ``C``.
|
||||||
|
|
||||||
|
The base type of the unchecked array may not contain any GC'ed memory but this
|
||||||
|
is currently not checked.
|
||||||
|
|
||||||
|
**Future directions**: GC'ed memory should be allowed in unchecked arrays and
|
||||||
|
there should be an explicit annotation of how the GC is to determine the
|
||||||
|
runtime size of the array.
|
||||||
|
|
||||||
|
|
||||||
Dynlib pragma for import
|
Dynlib pragma for import
|
||||||
------------------------
|
------------------------
|
||||||
With the `dynlib`:idx: pragma a procedure or a variable can be imported from
|
With the `dynlib`:idx: pragma a procedure or a variable can be imported from
|
||||||
|
|
|
||||||
|
|
@ -285,8 +285,8 @@ static N_INLINE(NI32, float32ToInt32)(float x) {
|
||||||
|
|
||||||
typedef struct TStringDesc* string;
|
typedef struct TStringDesc* string;
|
||||||
|
|
||||||
/* declared size of a sequence: */
|
/* declared size of a sequence/variable length array: */
|
||||||
#if defined(__GNUC__)
|
#if defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER)
|
||||||
# define SEQ_DECL_SIZE /* empty is correct! */
|
# define SEQ_DECL_SIZE /* empty is correct! */
|
||||||
#else
|
#else
|
||||||
# define SEQ_DECL_SIZE 1000000
|
# define SEQ_DECL_SIZE 1000000
|
||||||
|
|
|
||||||
3
todo.txt
3
todo.txt
|
|
@ -1,7 +1,6 @@
|
||||||
version 0.9.4
|
version 0.9.4
|
||||||
=============
|
=============
|
||||||
|
|
||||||
- implement unchecked arrays
|
|
||||||
- make testament produce full JSON information
|
- make testament produce full JSON information
|
||||||
- fix gensym capture bug
|
- fix gensym capture bug
|
||||||
- vm
|
- vm
|
||||||
|
|
@ -28,7 +27,7 @@ version 0.9.x
|
||||||
=============
|
=============
|
||||||
|
|
||||||
- memory manager: add a measure of fragmentation
|
- memory manager: add a measure of fragmentation
|
||||||
- implement 'union' and 'bits' pragmas
|
- implement 'bits' pragmas
|
||||||
- fix closures/lambdalifting
|
- fix closures/lambdalifting
|
||||||
- ensure (ref T)(a, b) works as a type conversion and type constructor
|
- ensure (ref T)(a, b) works as a type conversion and type constructor
|
||||||
- optimize 'genericReset'; 'newException' leads to code bloat
|
- optimize 'genericReset'; 'newException' leads to code bloat
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue