Working test cases for the sophisticated matrix library example from the manual

Fixed the dot operator when used within return types (see tgenericdotrettype)
Fixed the matching of generic concepts aliases used with the implicit generics style
This commit is contained in:
Zahary Karadjov 2016-08-31 02:17:14 +03:00
commit cbf66e99a8
12 changed files with 211 additions and 53 deletions

View file

@ -911,8 +911,7 @@ proc makeDeref(n: PNode): PNode =
t = skipTypes(baseTyp, {tyGenericInst, tyAlias}) t = skipTypes(baseTyp, {tyGenericInst, tyAlias})
const const
tyTypeParamsHolders = {tyGenericInst, tyCompositeTypeClass, tyTypeParamsHolders = {tyGenericInst, tyCompositeTypeClass}
tyUserTypeClass, tyUserTypeClassInst}
tyDotOpTransparent = {tyVar, tyPtr, tyRef, tyAlias} tyDotOpTransparent = {tyVar, tyPtr, tyRef, tyAlias}
proc readTypeParameter(c: PContext, typ: PType, proc readTypeParameter(c: PContext, typ: PType,
@ -1107,6 +1106,23 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
var ty = n.sons[0].typ var ty = n.sons[0].typ
var f: PSym = nil var f: PSym = nil
result = nil result = nil
template tryReadingGenericParam(t: PType) =
case t.kind
of tyTypeParamsHolders:
return readTypeParameter(c, t, i, n.info)
of tyUserTypeClasses:
if t.isResolvedUserTypeClass:
return readTypeParameter(c, t, i, n.info)
else:
n.typ = makeTypeFromExpr(c, copyTree(n))
return n
of tyGenericParam:
n.typ = makeTypeFromExpr(c, copyTree(n))
return n
else:
discard
if isTypeExpr(n.sons[0]) or (ty.kind == tyTypeDesc and ty.base.kind != tyNone): if isTypeExpr(n.sons[0]) or (ty.kind == tyTypeDesc and ty.base.kind != tyNone):
if ty.kind == tyTypeDesc: ty = ty.base if ty.kind == tyTypeDesc: ty = ty.base
ty = ty.skipTypes(tyDotOpTransparent) ty = ty.skipTypes(tyDotOpTransparent)
@ -1124,8 +1140,6 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
markUsed(n.info, f, c.graph.usageSym) markUsed(n.info, f, c.graph.usageSym)
styleCheckUse(n.info, f) styleCheckUse(n.info, f)
return return
of tyTypeParamsHolders:
return readTypeParameter(c, ty, i, n.info)
of tyObject, tyTuple: of tyObject, tyTuple:
if ty.n != nil and ty.n.kind == nkRecList: if ty.n != nil and ty.n.kind == nkRecList:
let field = lookupInRecord(ty.n, i) let field = lookupInRecord(ty.n, i)
@ -1134,8 +1148,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
n.typ.n = copyTree(n) n.typ.n = copyTree(n)
return n return n
else: else:
# echo "TYPE FIELD ACCESS" tryReadingGenericParam(ty)
# debug ty
return return
# XXX: This is probably not relevant any more # XXX: This is probably not relevant any more
# reset to prevent 'nil' bug: see "tests/reject/tenumitems.nim": # reset to prevent 'nil' bug: see "tests/reject/tenumitems.nim":
@ -1178,8 +1191,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
# we didn't find any field, let's look for a generic param # we didn't find any field, let's look for a generic param
if result == nil: if result == nil:
let t = n.sons[0].typ.skipTypes(tyDotOpTransparent) let t = n.sons[0].typ.skipTypes(tyDotOpTransparent)
if t.kind in tyTypeParamsHolders: tryReadingGenericParam(t)
result = readTypeParameter(c, t, i, n.info)
proc dotTransformation(c: PContext, n: PNode): PNode = proc dotTransformation(c: PContext, n: PNode): PNode =
if isSymChoice(n.sons[1]): if isSymChoice(n.sons[1]):

View file

@ -845,9 +845,9 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
for i in 0 .. paramType.sonsLen - 2: for i in 0 .. paramType.sonsLen - 2:
if paramType.sons[i].kind == tyStatic: if paramType.sons[i].kind == tyStatic:
var x = copyNode(ast.emptyNode) var staticCopy = paramType.sons[i].exactReplica
x.typ = paramType.sons[i] staticCopy.flags.incl tfInferrableStatic
result.rawAddSon makeTypeFromExpr(c, x) # aka 'tyUnknown' result.rawAddSon staticCopy
else: else:
result.rawAddSon newTypeS(tyAnything, c) result.rawAddSon newTypeS(tyAnything, c)
@ -891,7 +891,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
allowMetaTypes = true) allowMetaTypes = true)
result = liftingWalk(expanded, true) result = liftingWalk(expanded, true)
of tyUserTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot: of tyUserTypeClasses, tyBuiltInTypeClass, tyAnd, tyOr, tyNot:
result = addImplicitGeneric(copyType(paramType, getCurrOwner(c), true)) result = addImplicitGeneric(copyType(paramType, getCurrOwner(c), true))
of tyGenericParam: of tyGenericParam:
@ -1357,6 +1357,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
else: result = semGeneric(c, n, s, prev) else: result = semGeneric(c, n, s, prev)
of nkDotExpr: of nkDotExpr:
let typeExpr = semExpr(c, n) let typeExpr = semExpr(c, n)
if typeExpr.typ.kind == tyFromExpr:
return typeExpr.typ
if typeExpr.typ.kind != tyTypeDesc: if typeExpr.typ.kind != tyTypeDesc:
localError(n.info, errTypeExpected) localError(n.info, errTypeExpected)
result = errorType(c) result = errorType(c)

View file

@ -404,6 +404,8 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
case t.kind case t.kind
of tyGenericInvocation: of tyGenericInvocation:
result = handleGenericInvocation(cl, t) result = handleGenericInvocation(cl, t)
if result.lastSon.kind == tyUserTypeClass:
result.kind = tyUserTypeClassInst
of tyGenericBody: of tyGenericBody:
localError(cl.info, errCannotInstantiateX, typeToString(t)) localError(cl.info, errCannotInstantiateX, typeToString(t))

View file

@ -613,26 +613,34 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
template paramSym(kind): untyped = template paramSym(kind): untyped =
newSym(kind, typeParamName, Concept.sym, Concept.sym.info) newSym(kind, typeParamName, Concept.sym, Concept.sym.info)
case typ.kind block addTypeParam:
of tyStatic: for prev in typeParams:
param = paramSym skConst if prev[1].id == typ.id:
param.typ = typ.exactReplica param = paramSym prev[0].kind
if typ.n == nil: param.typ = prev[0].typ
param.typ.flags.incl tfInferrableStatic break addTypeParam
case typ.kind
of tyStatic:
param = paramSym skConst
param.typ = typ.exactReplica
if typ.n == nil:
param.typ.flags.incl tfInferrableStatic
else:
param.ast = typ.n
of tyUnknown:
param = paramSym skVar
param.typ = typ.exactReplica
else: else:
param.ast = typ.n param = paramSym skType
of tyUnknown: param.typ = if typ.isMetaType:
param = paramSym skVar c.newTypeWithSons(tyInferred, @[typ])
param.typ = typ.exactReplica else:
else: makeTypeDesc(c, typ)
param = paramSym skType
param.typ = if typ.isMetaType: typeParams.safeAdd((param, typ))
c.newTypeWithSons(tyInferred, @[typ])
else:
makeTypeDesc(c, typ)
addDecl(c, param) addDecl(c, param)
typeParams.safeAdd((param, typ))
for param in Concept.n[0]: for param in Concept.n[0]:
var var
@ -696,7 +704,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
put(m.bindings, p[1], p[0].typ) put(m.bindings, p[1], p[0].typ)
if ff.kind == tyUserTypeClassInst: if ff.kind == tyUserTypeClassInst:
result = generateTypeInstance(c, m.bindings, ff.sym.info, ff) result = generateTypeInstance(c, m.bindings, Concept.sym.info, ff)
else: else:
result = copyType(ff, ff.owner, true) result = copyType(ff, ff.owner, true)

View file

@ -183,7 +183,7 @@ proc iterOverTypeAux(marker: var IntSet, t: PType, iter: TTypeIter,
if result: return if result: return
if not containsOrIncl(marker, t.id): if not containsOrIncl(marker, t.id):
case t.kind case t.kind
of tyGenericInst, tyGenericBody, tyAlias: of tyGenericInst, tyGenericBody, tyAlias, tyInferred:
result = iterOverTypeAux(marker, lastSon(t), iter, closure) result = iterOverTypeAux(marker, lastSon(t), iter, closure)
else: else:
for i in countup(0, sonsLen(t) - 1): for i in countup(0, sonsLen(t) - 1):
@ -1320,6 +1320,9 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
if result < 0: return if result < 0: return
if a < maxAlign: a = maxAlign if a < maxAlign: a = maxAlign
result = align(result, a) result = align(result, a)
of tyInferred:
if typ.len > 1:
result = computeSizeAux(typ.lastSon, a)
of tyGenericInst, tyDistinct, tyGenericBody, tyAlias: of tyGenericInst, tyDistinct, tyGenericBody, tyAlias:
result = computeSizeAux(lastSon(typ), a) result = computeSizeAux(lastSon(typ), a)
of tyTypeClasses: of tyTypeClasses:
@ -1351,18 +1354,17 @@ proc getSize(typ: PType): BiggestInt =
if result < 0: internalError("getSize: " & $typ.kind) if result < 0: internalError("getSize: " & $typ.kind)
proc containsGenericTypeIter(t: PType, closure: RootRef): bool = proc containsGenericTypeIter(t: PType, closure: RootRef): bool =
if t.kind == tyStatic: case t.kind
of tyStatic:
return t.n == nil return t.n == nil
of tyTypeDesc:
if t.kind == tyTypeDesc:
if t.base.kind == tyNone: return true if t.base.kind == tyNone: return true
if containsGenericTypeIter(t.base, closure): return true if containsGenericTypeIter(t.base, closure): return true
return false return false
of GenericTypes + tyTypeClasses + {tyFromExpr}:
if t.kind in GenericTypes + tyTypeClasses + {tyFromExpr}:
return true return true
else:
return false return false
proc containsGenericType*(t: PType): bool = proc containsGenericType*(t: PType): bool =
result = iterOverType(t, containsGenericTypeIter, nil) result = iterOverType(t, containsGenericTypeIter, nil)

View file

@ -1678,7 +1678,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
elif sfImportc in s.flags: c.importcSym(n.info, s) elif sfImportc in s.flags: c.importcSym(n.info, s)
genLit(c, n, dest) genLit(c, n, dest)
of skConst: of skConst:
gen(c, s.ast, dest) let constVal = if s.ast != nil: s.ast else: s.typ.n
gen(c, constVal, dest)
of skEnumField: of skEnumField:
if dest < 0: dest = c.getTemp(n.typ) if dest < 0: dest = c.getTemp(n.typ)
if s.position >= low(int16) and s.position <= high(int16): if s.position >= low(int16) and s.position <= high(int16):

View file

@ -305,6 +305,9 @@ The concept types can be parametric just like the regular generic types:
.. code-block:: nim .. code-block:: nim
### matrixalgo.nim ### matrixalgo.nim
import typetraits
type type
AnyMatrix*[R, C: static[int]; T] = concept m, var mvar, type M AnyMatrix*[R, C: static[int]; T] = concept m, var mvar, type M
M.ValueType is T M.ValueType is T
@ -314,9 +317,13 @@ The concept types can be parametric just like the regular generic types:
m[int, int] is T m[int, int] is T
mvar[int, int] = T mvar[int, int] = T
type TransposedType = StripGenericParams(M)[C, R, T]
AnySquareMatrix*[N: static[int], T] = AnyMatrix[N, N, T] AnySquareMatrix*[N: static[int], T] = AnyMatrix[N, N, T]
proc transpose*[R, C, T](m: AnyMatrix[R, C, T]): m.type.basis[C, R, T] = AnyTransform3D* = AnyMatrix[4, 4, float]
proc transposed*(m: AnyMatrix): m.TransposedType =
for r in 0 .. <m.R: for r in 0 .. <m.R:
for c in 0 .. <m.C: for c in 0 .. <m.C:
result[r, c] = m[c, r] result[r, c] = m[c, r]
@ -324,6 +331,9 @@ The concept types can be parametric just like the regular generic types:
proc determinant*(m: AnySquareMatrix): int = proc determinant*(m: AnySquareMatrix): int =
... ...
proc setPerspectiveProjection*(m: AnyTransform3D) =
...
-------------- --------------
### matrix.nim ### matrix.nim
@ -331,11 +341,11 @@ The concept types can be parametric just like the regular generic types:
Matrix*[M, N: static[int]; T] = object Matrix*[M, N: static[int]; T] = object
data: array[M*N, T] data: array[M*N, T]
proc `[]`*(m: Matrix; m, n: int): m.T = proc `[]`*(M: Matrix; m, n: int): M.T =
m.data[m * m.N + n] M.data[m * M.N + n]
proc `[]=`*(m: var Matrix; m, n: int; v: m.T) = proc `[]=`*(M: var Matrix; m, n: int; v: M.T) =
m.data[m * m.N + n] = v M.data[m * M.N + n] = v
# Adapt the Matrix type to the concept's requirements # Adapt the Matrix type to the concept's requirements
template Rows*(M: type Matrix): expr = M.M template Rows*(M: type Matrix): expr = M.M
@ -347,8 +357,12 @@ The concept types can be parametric just like the regular generic types:
import matrix, matrixalgo import matrix, matrixalgo
var v: Matrix[3, 3, int] var
echo v.determinant m: Matrix[3, 3, int]
projectionMatrix: Matrix[4, 4, float]
echo m.transposed.determinant
setPerspectiveProjection projectionMatrix
When the concept type is matched against a concrete type, the unbound type When the concept type is matched against a concrete type, the unbound type
parameters are inferred from the body of the concept in a way that closely parameters are inferred from the body of the concept in a way that closely

15
tests/concepts/matrix.nim Normal file
View file

@ -0,0 +1,15 @@
type
Matrix*[M, N: static[int]; T] = object
data: array[M*N, T]
proc `[]`*(M: Matrix; m, n: int): M.T =
M.data[m * M.N + n]
proc `[]=`*(M: var Matrix; m, n: int; v: M.T) =
M.data[m * M.N + n] = v
# Adapt the Matrix type to the concept's requirements
template Rows*(M: type Matrix): expr = M.M
template Cols*(M: type Matrix): expr = M.N
template ValueType*(M: type Matrix): typedesc = M.T

View file

@ -0,0 +1,28 @@
import typetraits
type
AnyMatrix*[R, C: static[int]; T] = concept m, var mvar, type M
M.ValueType is T
M.Rows == R
M.Cols == C
m[int, int] is T
mvar[int, int] = T
type TransposedType = StripGenericParams(M)[C, R, T]
AnySquareMatrix*[N: static[int], T] = AnyMatrix[N, N, T]
AnyTransform3D* = AnyMatrix[4, 4, float]
proc transposed*(m: AnyMatrix): m.TransposedType =
for r in 0 .. <m.R:
for c in 0 .. <m.C:
result[r, c] = m[c, r]
proc determinant*(m: AnySquareMatrix): int =
return 0
proc setPerspectiveProjection*(m: AnyTransform3D) =
discard

View file

@ -1,7 +1,8 @@
discard """ discard """
output: "0\n0" output: "0\n0\n0"
msg: ''' msg: '''
R=3 C=3 TE=9 FF=14 FC=20 T=int R=3 C=3 TE=9 FF=14 FC=20 T=int
R=3 C=3 T=int
''' '''
""" """
@ -41,13 +42,25 @@ proc `[]=`(m: var MyMatrix; r, c: int, v: m.T) =
proc foo(x: MyMatrix, arr: array[15, x.T]) = discard proc foo(x: MyMatrix, arr: array[15, x.T]) = discard
proc matrixProc[R, C, TE, FF, FC, T](m: Matrix[R, C, TE, FF, FC, T]): T = proc genericMatrixProc[R, C, TE, FF, FC, T](m: Matrix[R, C, TE, FF, FC, T]): T =
static: static:
echo "R=", R, " C=", C, " TE=", TE, " FF=", FF, " FC=", FC, " T=", T.name echo "R=", R, " C=", C, " TE=", TE, " FF=", FF, " FC=", FC, " T=", T.name
m[0, 0] m[0, 0]
proc myMatrixProc(x: MyMatrix): MyMatrix.T = matrixProc(x) proc implicitMatrixProc(m: Matrix): m.T =
static:
echo "R=", m.Rows,
" C=", m.Cols,
# XXX: fix these
#" TE=", m.TotalElements,
#" FF=", m.FromFoo,
#" FC=", m.FromConst,
" T=", m.T.name
m[0, 0]
proc myMatrixProc(x: MyMatrix): MyMatrix.T = genericMatrixProc(x)
var x: MyMatrix[3, 3, int] var x: MyMatrix[3, 3, int]
@ -63,5 +76,6 @@ static:
no x is Matrix[3, 4, 9, 15, 20, int] no x is Matrix[3, 4, 9, 15, 20, int]
echo x.myMatrixProc echo x.myMatrixProc
echo x.matrixProc echo x.genericMatrixProc
echo x.implicitMatrixProc

View file

@ -0,0 +1,31 @@
discard """
output: "0"
"""
import matrix, matrixalgo
import typetraits # XXX: this should be removed
var m: Matrix[3, 3, int]
var projectionMatrix: Matrix[4, 4, float]
echo m.transposed.determinant
setPerspectiveProjection projectionMatrix
template ok(x) = assert x
template no(x) = assert(not x)
static:
ok projectionMatrix is AnyTransform3D
no m is AnyTransform3D
type SquareStringMatrix = Matrix[5, 5, string]
ok SquareStringMatrix is AnyMatrix
ok SquareStringMatrix is AnySquareMatrix
no SquareStringMatrix is AnyTransform3D
ok Matrix[5, 10, int] is AnyMatrix
no Matrix[7, 15, float] is AnySquareMatrix
no Matrix[4, 4, int] is AnyTransform3D

View file

@ -0,0 +1,29 @@
discard """
output: '''string
int
(int, string)
'''
"""
import typetraits
type
Foo[T, U] = object
x: T
y: U
proc bar[T](a: T): T.U =
echo result.type.name
proc bas(x: auto): x.T =
echo result.type.name
proc baz(x: Foo): (Foo.T, x.U) =
echo result.type.name
var
f: Foo[int, string]
x = bar f
z = bas f
y = baz f