Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

delay size pragma for generic types, use it for C++ Atomic[T] #24204

Open
wants to merge 3 commits into
base: devel
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions compiler/ast.nim
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,8 @@ type
tfVarIsPtr, # 'var' type is translated like 'ptr' even in C++ mode
tfHasMeta, # type contains "wildcard" sub-types such as generic params
# or other type classes
# also used when checking pragmas of forward types
# to signify that the forward type is generic
tfHasGCedMem, # type contains GC'ed memory
tfPacked
tfHasStatic
Expand Down Expand Up @@ -399,6 +401,9 @@ type
tfIsOutParam
tfSendable
tfImplicitStatic
tfHasUnresolvedProperties
## for types that have type properties like `size` that depend on
## generic parameters

TTypeFlags* = set[TTypeFlag]

Expand Down
1 change: 1 addition & 0 deletions compiler/condsyms.nim
Original file line number Diff line number Diff line change
Expand Up @@ -169,4 +169,5 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimHasGenericsOpenSym2")
defineSymbol("nimHasGenericsOpenSym3")
defineSymbol("nimHasJsNoLambdaLifting")
defineSymbol("nimHasGenericSize")
defineSymbol("nimHasDefaultFloatRoundtrip")
28 changes: 19 additions & 9 deletions compiler/pragmas.nim
Original file line number Diff line number Diff line change
Expand Up @@ -943,16 +943,26 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
processImportObjC(c, sym, getOptionalStr(c, it, "$1"), it.info)
of wSize:
if sym.typ == nil: invalidPragma(c, it)
var size = expectIntLit(c, it)
case size
of 1, 2, 4:
sym.typ.size = size
sym.typ.align = int16 size
of 8:
sym.typ.size = 8
sym.typ.align = floatInt64Align(c.config)
if sym.typ.kind == tyForward and tfHasMeta in sym.typ.flags:
# generic forward type, value possibly depends on generic types
# but generic type symbols aren't defined yet, so assume they are
# and ignore expression for now
sym.typ.flags.incl tfHasUnresolvedProperties
else:
localError(c.config, it.info, "size may only be 1, 2, 4 or 8")
var size = expectIntLit(c, it)
if sfImportc in sym.flags:
# no restrictions on size for imported types
setImportedTypeSize(c.config, sym.typ, size)
else:
case size
of 1, 2, 4:
sym.typ.size = size
sym.typ.align = int16 size
of 8:
sym.typ.size = 8
sym.typ.align = floatInt64Align(c.config)
else:
localError(c.config, it.info, "size may only be 1, 2, 4 or 8")
of wAlign:
let alignment = expectIntLit(c, it)
if isPowerOfTwo(alignment) and alignment > 0:
Expand Down
10 changes: 9 additions & 1 deletion compiler/semstmts.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1445,7 +1445,12 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) =
s = semIdentDef(c, name, skType)
onDef(name.info, s)
s.typ = newTypeS(tyForward, c)
s.typ.sym = s # process pragmas:
let isGeneric = typeDef[1].kind != nkEmpty
if isGeneric:
# mark as tfHasMeta for pragmas to know this type is generic
s.typ.flags.incl tfHasMeta
s.typ.sym = s
# process pragmas:
if name.kind == nkPragmaExpr:
let rewritten = applyTypeSectionPragmas(c, name[1], typeDef)
if rewritten != nil:
Expand All @@ -1458,6 +1463,9 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) =
typeDefLeftSidePass(c, typeSection, i)
return
pragma(c, s, name[1], typePragmas)
if isGeneric:
# remove previously set tfHasMeta to mark generic parameters
s.typ.flags.excl tfHasMeta
if sfForward in s.flags:
# check if the symbol already exists:
let pkg = c.module.owner
Expand Down
32 changes: 30 additions & 2 deletions compiler/semtypinst.nim
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@
import std / tables

import ast, astalgo, msgs, types, magicsys, semdata, renderer, options,
lineinfos, modulegraphs, layeredtable
lineinfos, modulegraphs, layeredtable, trees, wordrecg

when defined(nimPreviewSlimSystem):
import std/assertions

const tfInstClearedFlags = {tfHasMeta, tfUnresolved}
const tfInstClearedFlags = {tfHasMeta, tfUnresolved, tfHasUnresolvedProperties}

proc checkPartialConstructedType(conf: ConfigRef; info: TLineInfo, t: PType) =
if t.kind in {tyVar, tyLent} and t.elementType.kind in {tyVar, tyLent}:
Expand Down Expand Up @@ -573,6 +573,27 @@ proc propagateFieldFlags(t: PType, n: PNode) =
propagateFieldFlags(t, son)
else: discard

proc computeUnresolvedProperties(cl: var TReplTypeVars, result, t: PType) =
assert t.sym != nil and t.sym.ast != nil and t.sym.ast[0].kind == nkPragmaExpr
let pragmas = t.sym.ast[0][1]
for prag in pragmas:
let key = whichPragma(prag)
case key
of wSize:
if prag.kind notin nkPragmaCallKinds or prag.len != 2:
localError(cl.c.config, prag.info, "expected argument for `size` pragma")
var e = prepareNode(cl, prag[1])
e = cl.c.semConstExpr(cl.c, e)
var val = 0
case e.kind
of nkIntLit..nkInt64Lit:
val = int(e.intVal)
else:
localError(cl.c.config, e.info, "integer value expected for `size`")
setImportedTypeSize(cl.c.config, result, val)
else: discard
result.flags.excl tfHasUnresolvedProperties

proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
template bailout =
if (t.sym == nil) or (t.sym != nil and sfGeneratedType in t.sym.flags):
Expand Down Expand Up @@ -752,6 +773,8 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
result.setIndexType result.indexType.skipTypes({tyStatic, tyDistinct})

else: discard
if not cl.allowMetaTypes and tfHasUnresolvedProperties in result.flags:
computeUnresolvedProperties(cl, result, t)
else:
# If this type doesn't refer to a generic type we may still want to run it
# trough replaceObjBranches in order to resolve any pending nkRecWhen nodes
Expand All @@ -765,6 +788,11 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
# Invalidate the type size as we may alter its structure
result.size = -1
result.n = replaceObjBranches(cl, result.n)
if not cl.allowMetaTypes and tfHasUnresolvedProperties in result.flags:
# this can be reached for empty `object` generic bodies
result = instCopyType(cl, result)
result.size = -1 # needs to be recomputed
computeUnresolvedProperties(cl, result, t)

proc initTypeVars*(p: PContext, typeMap: LayeredIdTable, info: TLineInfo;
owner: PSym): TReplTypeVars =
Expand Down
11 changes: 11 additions & 0 deletions compiler/types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1483,6 +1483,17 @@ proc getSize*(conf: ConfigRef; typ: PType): BiggestInt =
computeSizeAlign(conf, typ)
result = typ.size

proc setImportedTypeSize*(conf: ConfigRef, t: PType, size: int) =
t.size = size
if tfPacked in t.flags or size <= 1:
t.align = 1
elif size <= 2:
t.align = 2
elif size <= 4:
t.align = 4
else:
t.align = floatInt64Align(conf)

proc containsGenericTypeIter(t: PType, closure: RootRef): bool =
case t.kind
of tyStatic:
Expand Down
4 changes: 2 additions & 2 deletions doc/manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -7720,6 +7720,8 @@ The `size pragma` allows specifying the size of the enum type.
doAssert sizeof(EventType) == sizeof(uint32)
```

For this purpose, the `size pragma` accepts only the values 1, 2, 4 or 8.

The `size pragma` can also specify the size of an `importc` incomplete object type
so that one can get the size of it at compile time even if it was declared without fields.

Expand All @@ -7732,8 +7734,6 @@ so that one can get the size of it at compile time even if it was declared witho
echo sizeof(AtomicFlag)
```

The `size pragma` accepts only the values 1, 2, 4 or 8.


Align pragma
------------
Expand Down
8 changes: 6 additions & 2 deletions lib/pure/concurrency/atomics.nim
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,15 @@ when (defined(cpp) and defined(nimUseCppAtomics)) or defined(nimdoc):
## Also guarantees that all threads observe the same total ordering
## with other moSequentiallyConsistent operations.

type
Atomic*[T] {.importcpp: "std::atomic", completeStruct.} = object
when defined(nimHasGenericSize):
type Atomic*[T] {.importcpp: "std::atomic", size: sizeof(T).} = object
## An atomic object with underlying type `T`.
else:
type Atomic*[T] {.importcpp: "std::atomic", completeStruct.} = object
## An atomic object with underlying type `T`.
raw: T

type
AtomicFlag* {.importcpp: "std::atomic_flag", size: 1.} = object
## An atomic boolean state.

Expand Down
14 changes: 14 additions & 0 deletions tests/ccgbugs/tatomictypeinfo.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
discard """
matrix: "--mm:refc; --mm:orc"
targets: "c cpp"
"""

# issue #24159

import std/atomics

type N = object
u: ptr Atomic[int]
proc w(args: N) = discard
var e: Thread[N]
createThread(e, w, default(N))
1 change: 1 addition & 0 deletions tests/ccgbugs/tcgbug.nim
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ success
M1 M2
ok
'''
targets: "c cpp"
matrix: "--mm:refc;--mm:orc"
"""

Expand Down