Static[T] fixes (#7333)

* fix the usage of unresolved static[T] parameters in proc signatures
* fix tsametype and tmacrogenerics
* Allow creating composite type classes with concepts and using them in type signatures
* Allow integers to be used in ident concatenations
* Support using imported C++ generic types in proc signatures
* fixes #7230
* closes #7379
* re-enable some metatype tests
This commit is contained in:
zah 2018-03-24 16:28:09 +02:00 • committed by Andreas Rumpf
commit 121b9e26fb
18 changed files with 265 additions and 26 deletions

View file

@ -981,6 +981,7 @@ const
nkPragmaCallKinds* = {nkExprColonExpr, nkCall, nkCallStrLit} nkPragmaCallKinds* = {nkExprColonExpr, nkCall, nkCallStrLit}
nkLiterals* = {nkCharLit..nkTripleStrLit} nkLiterals* = {nkCharLit..nkTripleStrLit}
nkFloatLiterals* = {nkFloatLit..nkFloat128Lit}
nkLambdaKinds* = {nkLambda, nkDo} nkLambdaKinds* = {nkLambda, nkDo}
declarativeDefs* = {nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef, nkConverterDef} declarativeDefs* = {nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef, nkConverterDef}
procDefs* = nkLambdaKinds + declarativeDefs procDefs* = nkLambdaKinds + declarativeDefs
@ -1476,7 +1477,7 @@ proc copyNode*(src: PNode): PNode =
echo "COMES FROM ", src.id echo "COMES FROM ", src.id
case src.kind case src.kind
of nkCharLit..nkUInt64Lit: result.intVal = src.intVal of nkCharLit..nkUInt64Lit: result.intVal = src.intVal
of nkFloatLit..nkFloat128Lit: result.floatVal = src.floatVal of nkFloatLiterals: result.floatVal = src.floatVal
of nkSym: result.sym = src.sym of nkSym: result.sym = src.sym
of nkIdent: result.ident = src.ident of nkIdent: result.ident = src.ident
of nkStrLit..nkTripleStrLit: result.strVal = src.strVal of nkStrLit..nkTripleStrLit: result.strVal = src.strVal
@ -1495,7 +1496,7 @@ proc shallowCopy*(src: PNode): PNode =
echo "COMES FROM ", src.id echo "COMES FROM ", src.id
case src.kind case src.kind
of nkCharLit..nkUInt64Lit: result.intVal = src.intVal of nkCharLit..nkUInt64Lit: result.intVal = src.intVal
of nkFloatLit..nkFloat128Lit: result.floatVal = src.floatVal of nkFloatLiterals: result.floatVal = src.floatVal
of nkSym: result.sym = src.sym of nkSym: result.sym = src.sym
of nkIdent: result.ident = src.ident of nkIdent: result.ident = src.ident
of nkStrLit..nkTripleStrLit: result.strVal = src.strVal of nkStrLit..nkTripleStrLit: result.strVal = src.strVal
@ -1515,7 +1516,7 @@ proc copyTree*(src: PNode): PNode =
echo "COMES FROM ", src.id echo "COMES FROM ", src.id
case src.kind case src.kind
of nkCharLit..nkUInt64Lit: result.intVal = src.intVal of nkCharLit..nkUInt64Lit: result.intVal = src.intVal
of nkFloatLit..nkFloat128Lit: result.floatVal = src.floatVal of nkFloatLiterals: result.floatVal = src.floatVal
of nkSym: result.sym = src.sym of nkSym: result.sym = src.sym
of nkIdent: result.ident = src.ident of nkIdent: result.ident = src.ident
of nkStrLit..nkTripleStrLit: result.strVal = src.strVal of nkStrLit..nkTripleStrLit: result.strVal = src.strVal
@ -1564,7 +1565,7 @@ proc getInt*(a: PNode): BiggestInt =
proc getFloat*(a: PNode): BiggestFloat = proc getFloat*(a: PNode): BiggestFloat =
case a.kind case a.kind
of nkFloatLit..nkFloat128Lit: result = a.floatVal of nkFloatLiterals: result = a.floatVal
else: else:
internalError(a.info, "getFloat") internalError(a.info, "getFloat")
result = 0.0 result = 0.0

View file

@ -842,6 +842,13 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
# always call for sideeffects: # always call for sideeffects:
assert t.kind != tyTuple assert t.kind != tyTuple
discard getRecordDesc(m, t, result, check) discard getRecordDesc(m, t, result, check)
# The resulting type will include commas and these won't play well
# with the C macros for defining procs such as N_NIMCALL. We must
# create a typedef for the type and use it in the proc signature:
let typedefName = ~"TY" & $sig
addf(m.s[cfsTypes], "typedef $1 $2;$n", [result, typedefName])
m.typeCache[sig] = typedefName
result = typedefName
else: else:
when false: when false:
if t.sym != nil and t.sym.name.s == "KeyValuePair": if t.sym != nil and t.sym.name.s == "KeyValuePair":

View file

@ -44,6 +44,7 @@ proc considerQuotedIdent*(n: PNode, origin: PNode = nil): PIdent =
case x.kind case x.kind
of nkIdent: id.add(x.ident.s) of nkIdent: id.add(x.ident.s)
of nkSym: id.add(x.sym.name.s) of nkSym: id.add(x.sym.name.s)
of nkLiterals - nkFloatLiterals: id.add(x.renderTree)
else: handleError(n, origin) else: handleError(n, origin)
result = getIdent(id) result = getIdent(id)
of nkOpenSymChoice, nkClosedSymChoice: of nkOpenSymChoice, nkClosedSymChoice:

View file

@ -884,6 +884,9 @@ const
proc readTypeParameter(c: PContext, typ: PType, proc readTypeParameter(c: PContext, typ: PType,
paramName: PIdent, info: TLineInfo): PNode = paramName: PIdent, info: TLineInfo): PNode =
# Note: This function will return emptyNode when attempting to read
# a static type parameter that is not yet resolved (e.g. this may
# happen in proc signatures such as `proc(x: T): array[T.sizeParam, U]`
if typ.kind in {tyUserTypeClass, tyUserTypeClassInst}: if typ.kind in {tyUserTypeClass, tyUserTypeClassInst}:
for statement in typ.n: for statement in typ.n:
case statement.kind case statement.kind
@ -914,7 +917,10 @@ proc readTypeParameter(c: PContext, typ: PType,
if tParam.sym.name.id == paramName.id: if tParam.sym.name.id == paramName.id:
let rawTyp = ty.sons[s + 1] let rawTyp = ty.sons[s + 1]
if rawTyp.kind == tyStatic: if rawTyp.kind == tyStatic:
return rawTyp.n if rawTyp.n != nil:
return rawTyp.n
else:
return emptyNode
else: else:
let foundTyp = makeTypeDesc(c, rawTyp) let foundTyp = makeTypeDesc(c, rawTyp)
return newSymNode(copySym(tParam.sym).linkTo(foundTyp), info) return newSymNode(copySym(tParam.sym).linkTo(foundTyp), info)
@ -1079,21 +1085,43 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
template tryReadingGenericParam(t: PType) = template tryReadingGenericParam(t: PType) =
case t.kind case t.kind
of tyTypeParamsHolders: of tyTypeParamsHolders:
return readTypeParameter(c, t, i, n.info) result = readTypeParameter(c, t, i, n.info)
if result == emptyNode:
result = n
n.typ = makeTypeFromExpr(c, n.copyTree)
return
of tyUserTypeClasses: of tyUserTypeClasses:
if t.isResolvedUserTypeClass: if t.isResolvedUserTypeClass:
return readTypeParameter(c, t, i, n.info) return readTypeParameter(c, t, i, n.info)
else: else:
n.typ = makeTypeFromExpr(c, copyTree(n)) n.typ = makeTypeFromExpr(c, copyTree(n))
return n return n
of tyGenericParam: of tyGenericParam, tyAnything:
n.typ = makeTypeFromExpr(c, copyTree(n)) n.typ = makeTypeFromExpr(c, copyTree(n))
return n return n
else: else:
discard discard
if isTypeExpr(n.sons[0]) or (ty.kind == tyTypeDesc and ty.base.kind != tyNone): var argIsType = false
if ty.kind == tyTypeDesc: ty = ty.base
if ty.kind == tyTypeDesc:
if ty.base.kind == tyNone:
# This is a still unresolved typedesc parameter.
# If this is a regular proc, then all bets are off and we must return
# tyFromExpr, but when this happen in a macro this is not a built-in
# field access and we leave the compiler to compile a normal call:
if getCurrOwner(c).kind != skMacro:
n.typ = makeTypeFromExpr(c, n.copyTree)
return n
else:
return nil
else:
ty = ty.base
argIsType = true
else:
argIsType = isTypeExpr(n.sons[0])
if argIsType:
ty = ty.skipTypes(tyDotOpTransparent) ty = ty.skipTypes(tyDotOpTransparent)
case ty.kind case ty.kind
of tyEnum: of tyEnum:
@ -2186,7 +2214,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
# because of the changed symbol binding, this does not mean that we # because of the changed symbol binding, this does not mean that we
# don't have to check the symbol for semantics here again! # don't have to check the symbol for semantics here again!
result = semSym(c, n, n.sym, flags) result = semSym(c, n, n.sym, flags)
of nkEmpty, nkNone, nkCommentStmt: of nkEmpty, nkNone, nkCommentStmt, nkType:
discard discard
of nkNilLit: of nkNilLit:
if result.typ == nil: result.typ = getSysType(tyNil) if result.typ == nil: result.typ = getSysType(tyNil)

View file

@ -174,6 +174,8 @@ proc sideEffectsCheck(c: PContext, s: PSym) =
proc instGenericContainer(c: PContext, info: TLineInfo, header: PType, proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
allowMetaTypes = false): PType = allowMetaTypes = false): PType =
internalAssert header.kind == tyGenericInvocation
var var
typeMap: LayeredIdTable typeMap: LayeredIdTable
cl: TReplTypeVars cl: TReplTypeVars
@ -185,7 +187,35 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
cl.info = info cl.info = info
cl.c = c cl.c = c
cl.allowMetaTypes = allowMetaTypes cl.allowMetaTypes = allowMetaTypes
# We must add all generic params in scope, because the generic body
# may include tyFromExpr nodes depending on these generic params.
# XXX: This looks quite similar to the code in matchUserTypeClass,
# perhaps the code can be extracted in a shared function.
openScope(c)
let genericTyp = header.base
for i in 0 .. (genericTyp.len - 2):
let genParam = genericTyp[i]
var param: PSym
template paramSym(kind): untyped =
newSym(kind, genParam.sym.name, genericTyp.sym, genParam.sym.info)
if genParam.kind == tyStatic:
param = paramSym skConst
param.ast = header[i+1].n
param.typ = header[i+1]
else:
param = paramSym skType
param.typ = makeTypeDesc(c, header[i+1])
# this scope was not created by the user,
# unused params shoudn't be reported.
param.flags.incl sfUsed
addDecl(c, param)
result = replaceTypeVarsT(cl, header) result = replaceTypeVarsT(cl, header)
closeScope(c)
proc instantiateProcType(c: PContext, pt: TIdTable, proc instantiateProcType(c: PContext, pt: TIdTable,
prc: PSym, info: TLineInfo) = prc: PSym, info: TLineInfo) =

View file

@ -1397,7 +1397,10 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
fixupTypeOf(c, prev, typExpr) fixupTypeOf(c, prev, typExpr)
result = typExpr.typ result = typExpr.typ
else: else:
result = semTypeExpr(c, n, prev) if c.inGenericContext > 0 and n.kind == nkCall:
result = makeTypeFromExpr(c, n.copyTree)
else:
result = semTypeExpr(c, n, prev)
of nkWhenStmt: of nkWhenStmt:
var whenResult = semWhen(c, n, false) var whenResult = semWhen(c, n, false)
if whenResult.kind == nkStmtList: whenResult.kind = nkStmtListType if whenResult.kind == nkStmtList: whenResult.kind = nkStmtListType

View file

@ -460,6 +460,10 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
of tyFromExpr: of tyFromExpr:
if cl.allowMetaTypes: return if cl.allowMetaTypes: return
# This assert is triggered when a tyFromExpr was created in a cyclic
# way. You should break the cycle at the point of creation by introducing
# a call such as: `n.typ = makeTypeFromExpr(c, n.copyTree)`
# Otherwise, the cycle will be fatal for the prepareNode call below
assert t.n.typ != t assert t.n.typ != t
var n = prepareNode(cl, t.n) var n = prepareNode(cl, t.n)
if n.kind != nkEmpty: if n.kind != nkEmpty:

View file

@ -1884,7 +1884,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
return arg return arg
elif f.kind == tyTypeDesc: elif f.kind == tyTypeDesc:
return arg return arg
elif f.kind == tyStatic: elif f.kind == tyStatic and arg.typ.n != nil:
return arg.typ.n return arg.typ.n
else: else:
return argSemantized # argOrig return argSemantized # argOrig

View file

@ -1136,6 +1136,8 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
of tyTypeClasses: of tyTypeClasses:
if tfGenericTypeParam in t.flags or taConcept in flags: #or taField notin flags: if tfGenericTypeParam in t.flags or taConcept in flags: #or taField notin flags:
discard discard
elif t.isResolvedUserTypeClass:
result = typeAllowedAux(marker, t.lastSon, kind, flags)
elif kind notin {skParam, skResult}: elif kind notin {skParam, skResult}:
result = t result = t
of tyGenericBody, tyGenericParam, tyGenericInvocation, of tyGenericBody, tyGenericParam, tyGenericInvocation,

View file

@ -0,0 +1,19 @@
discard """
output: "1\n2\n3"
"""
type
MyConcept = concept x
someProc(x)
SomeSeq = seq[MyConcept]
proc someProc(x:int) = echo x
proc work (s: SomeSeq) =
for item in s:
someProc item
var s = @[1, 2, 3]
work s

View file

@ -300,7 +300,7 @@ reject wantsVarPointer2(pcat)
# covariance may be allowed for certain extern types # covariance may be allowed for certain extern types
{.emit: """ {.emit: """/*TYPESECTION*/
template <class T> struct FN { typedef void (*type)(T); }; template <class T> struct FN { typedef void (*type)(T); };
template <class T> struct ARR { typedef T DataType[2]; DataType data; }; template <class T> struct ARR { typedef T DataType[2]; DataType data; };
""".} """.}

View file

@ -2,7 +2,7 @@ discard """
targets: "cpp" targets: "cpp"
""" """
{.emit: """ {.emit: """/*TYPESECTION*/
template <class T> template <class T>
struct Vector { struct Vector {

View file

@ -14,7 +14,6 @@ AST b
20Test 20Test
20 20
''' '''
disabled: true
""" """
import macros import macros
@ -26,7 +25,7 @@ type
const data: Tconfig = (@["aa", "bb"], @[11, 22]) const data: Tconfig = (@["aa", "bb"], @[11, 22])
macro mymacro(data: static[TConfig]) = macro mymacro(data: static[TConfig]): untyped =
echo "letters" echo "letters"
for s in items(data.letters): for s in items(data.letters):
echo s echo s
@ -44,10 +43,10 @@ const
a : Ta = @[(11, 22), (33, 44)] a : Ta = @[(11, 22), (33, 44)]
b : Tb = (@[55,66], @[77, 88]) b : Tb = (@[55,66], @[77, 88])
macro mA(data: static[Ta]) = macro mA(data: static[Ta]): untyped =
echo "AST a \n", repr(data) echo "AST a \n", repr(data)
macro mB(data: static[Tb]) = macro mB(data: static[Tb]): untyped =
echo "AST b \n", repr(data) echo "AST b \n", repr(data)
echo data.e[0] echo data.e[0]
@ -57,13 +56,15 @@ mB(b)
type type
Foo[N: static[int], Z: static[string]] = object Foo[N: static[int], Z: static[string]] = object
macro staticIntMacro(f: static[int]) = echo f macro staticIntMacro(f: static[int]): untyped =
echo f
staticIntMacro 10 staticIntMacro 10
var var
x: Foo[20, "Test"] x: Foo[20, "Test"]
macro genericMacro[N; Z: static[string]](f: Foo[N, Z], ll = 3, zz = 12) = macro genericMacro[N; Z: static[string]](f: Foo[N, Z], ll = 3, zz = 12): untyped =
echo N, Z echo N, Z
genericMacro x genericMacro x

View file

@ -1,4 +1,9 @@
import macros discard """
output: "8\n8\n4"
"""
import
macros, typetraits
template selectType(x: int): typeDesc = template selectType(x: int): typeDesc =
when x < 10: when x < 10:
@ -11,6 +16,9 @@ template simpleTypeTempl: typeDesc =
macro typeFromMacro: typedesc = string macro typeFromMacro: typedesc = string
# The tests below check that the result variable of the
# selected type matches the literal types in the code:
proc t1*(x: int): simpleTypeTempl() = proc t1*(x: int): simpleTypeTempl() =
result = "test" result = "test"
@ -37,3 +45,57 @@ proc t6*(x: type(t3(0))): type(t1(0)) =
proc t7*(x: int): type($x) = proc t7*(x: int): type($x) =
result = "test" result = "test"
# This is a more compicated example involving a type
# selection through a macro:
# https://github.com/nim-lang/Nim/issues/7230
macro getBase*(bits: static[int]): untyped =
if bits >= 128:
result = newTree(nnkBracketExpr, ident("MpUintBase"), ident("uint64"))
else:
result = newTree(nnkBracketExpr, ident("MpUintBase"), ident("uint32"))
type
BaseUint* = SomeUnsignedInt or MpUintBase
MpUintBase*[T] = object
lo*, hi*: T
## This gets type mismatch
MpUint*[bits: static[int]] = getBase(bits)
var m1: MpUint[128]
var m2: MpUint[64]
var m3: getBase(32)
static:
# assert m1.type.name == "MpUintBase[uint64]"
assert m1.lo.type.name == "uint64"
assert m2.lo.type.name == "uint32"
assert m3.type.name == "MpUintBase[system.uint32]"
# https://github.com/nim-lang/Nim/issues/7379
import macros, typetraits
macro works(): untyped =
result = getType(int64)
macro fails(bits: static[int]): untyped =
if bits > 64:
result = getType(int64)
else:
result = getType(int32)
type
Foo*[bits: static[int]] = works()
Bar*[bits: static[int]] = fails(bits)
var a: Foo[16]
var b: Bar[256]
var c: Bar[32]
echo sizeof(a)
echo sizeof(b)
echo sizeof(c)

View file

@ -1,7 +1,6 @@
discard """ discard """
errormsg: "type mismatch: got <string> but expected 'ptr'" errormsg: "type mismatch: got <string> but expected 'ptr'"
line: 20 line: 20
disabled: true
""" """
import typetraits import typetraits
@ -12,7 +11,7 @@ type
var x = Vec([1, 2, 3]) var x = Vec([1, 2, 3])
static: static:
assert x.type.name == "Vec[static[int](3), int]" assert x.type.name == "Vec[3, system.int]"
var str1: string = "hello, world!" var str1: string = "hello, world!"
var ptr1: ptr = addr(str1) var ptr1: ptr = addr(str1)

View file

@ -0,0 +1,32 @@
type
Hash*[bits: static[int]] = object
data*: array[bits div 8, uint8]
{.emit: """
void sha_256(void* input, int input_len, void* output, int output_len) {}
void sha_512(void* input, int input_len, void* output, int output_len) {}
void keccak_256(void* input, int input_len, void* output, int output_len) {}
void keccak_512(void* input, int input_len, void* output, int output_len) {}
""".}
template defineKeccak(bits: untyped) =
proc `extKeccak bits`(output: pointer, outSize: csize, input: pointer, inputSize: csize) {.nodecl, importc: "keccak_" & astToStr(bits).}
template defineSha(bits: static[int]) =
proc `extSha bits`(output: pointer, outSize: csize, input: pointer, inputSize: csize) {.nodecl, importc: "sha_" & astToStr(bits).}
template defineHashProcs(bits) =
defineSha(bits)
defineKeccak(bits)
defineHashProcs(256)
defineHashProcs(512)
extSha256(nil, 0, nil, 0)
extSha512(nil, 0, nil, 0)
extKeccak256(nil, 0, nil, 0)
extKeccak512(nil, 0, nil, 0)

View file

@ -0,0 +1,44 @@
discard """
output: "Digest[128]\nDigest[256]"
"""
import typetraits
type
Digest[bits: static[int]] = object
data: array[bits div 8, byte]
ContextKind = enum
A, B, C
HashingContext[bits: static[int], kind: static[ContextKind]] = object
ctx: array[bits div 8, byte]
Hash128 = HashingContext[128, A]
Hash256 = HashingContext[256, B]
HMAC[HashType] = object
h: HashType
proc init(c: var HashingContext) = discard
proc update(c: var HashingContext, data: ptr byte, dataLen: uint) = discard
proc finish(c: var HashingContext): Digest[c.bits] = discard
proc digest(T: typedesc, data: ptr byte, dataLen: uint): Digest[T.bits] =
mixin init, update, finish
var ctx: T
ctx.init()
ctx.update(data, dataLen)
result = ctx.finish()
var h = Hash128.digest(nil, 0)
echo h.type.name
proc finish(hmac: var HMAC): Digest[HMAC.HashType.bits] =
discard
var hm: HMAC[Hash256]
var d = hm.finish
echo d.type.name

View file

@ -1,9 +1,9 @@
discard """ discard """
targets: "cpp" targets: "cpp"
output: "[0, 0, 10, 0]\n5\n1.2\n15\ntest" output: "[0, 0, 10, 0]\n5\n1.2\n15\ntest\n[0, 0, 20, 0]"
""" """
{.emit: """ {.emit: """/*TYPESECTION*/
template <int N, class T> template <int N, class T>
struct GenericIntType { struct GenericIntType {
@ -51,3 +51,9 @@ echo c.field
echo d.field echo d.field
echo e.field echo e.field
proc plus(a, b: GenInt4): GenInt4 =
for i in 0 ..< result.data.len:
result.data[i] = a.data[i] + b.data[i]
echo plus(a, a).data