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
parent 52b241fd57
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})
const
tyTypeParamsHolders = {tyGenericInst, tyCompositeTypeClass,
tyUserTypeClass, tyUserTypeClassInst}
tyTypeParamsHolders = {tyGenericInst, tyCompositeTypeClass}
tyDotOpTransparent = {tyVar, tyPtr, tyRef, tyAlias}
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 f: PSym = 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 ty.kind == tyTypeDesc: ty = ty.base
ty = ty.skipTypes(tyDotOpTransparent)
@@ -1124,8 +1140,6 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
markUsed(n.info, f, c.graph.usageSym)
styleCheckUse(n.info, f)
return
of tyTypeParamsHolders:
return readTypeParameter(c, ty, i, n.info)
of tyObject, tyTuple:
if ty.n != nil and ty.n.kind == nkRecList:
let field = lookupInRecord(ty.n, i)
@@ -1134,8 +1148,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
n.typ.n = copyTree(n)
return n
else:
# echo "TYPE FIELD ACCESS"
# debug ty
tryReadingGenericParam(ty)
return
# XXX: This is probably not relevant any more
# 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
if result == nil:
let t = n.sons[0].typ.skipTypes(tyDotOpTransparent)
if t.kind in tyTypeParamsHolders:
result = readTypeParameter(c, t, i, n.info)
tryReadingGenericParam(t)
proc dotTransformation(c: PContext, n: PNode): PNode =
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:
if paramType.sons[i].kind == tyStatic:
var x = copyNode(ast.emptyNode)
x.typ = paramType.sons[i]
result.rawAddSon makeTypeFromExpr(c, x) # aka 'tyUnknown'
var staticCopy = paramType.sons[i].exactReplica
staticCopy.flags.incl tfInferrableStatic
result.rawAddSon staticCopy
else:
result.rawAddSon newTypeS(tyAnything, c)
@@ -891,7 +891,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
allowMetaTypes = true)
result = liftingWalk(expanded, true)
of tyUserTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot:
of tyUserTypeClasses, tyBuiltInTypeClass, tyAnd, tyOr, tyNot:
result = addImplicitGeneric(copyType(paramType, getCurrOwner(c), true))
of tyGenericParam:
@@ -1357,6 +1357,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
else: result = semGeneric(c, n, s, prev)
of nkDotExpr:
let typeExpr = semExpr(c, n)
if typeExpr.typ.kind == tyFromExpr:
return typeExpr.typ
if typeExpr.typ.kind != tyTypeDesc:
localError(n.info, errTypeExpected)
result = errorType(c)

View File

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

View File

@@ -613,26 +613,34 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
template paramSym(kind): untyped =
newSym(kind, typeParamName, Concept.sym, Concept.sym.info)
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:
param = paramSym skType
param.typ = if typ.isMetaType:
c.newTypeWithSons(tyInferred, @[typ])
else:
makeTypeDesc(c, typ)
block addTypeParam:
for prev in typeParams:
if prev[1].id == typ.id:
param = paramSym prev[0].kind
param.typ = prev[0].typ
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:
param = paramSym skType
param.typ = if typ.isMetaType:
c.newTypeWithSons(tyInferred, @[typ])
else:
makeTypeDesc(c, typ)
typeParams.safeAdd((param, typ))
addDecl(c, param)
typeParams.safeAdd((param, typ))
for param in Concept.n[0]:
var
@@ -682,7 +690,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
diagnostics.add msg
var checkedBody = c.semTryExpr(c, body.copyTree, flags)
if collectDiagnostics:
writelnHook = oldWriteHook
for msg in diagnostics: m.diagnostics.safeAdd msg
@@ -696,7 +704,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
put(m.bindings, p[1], p[0].typ)
if ff.kind == tyUserTypeClassInst:
result = generateTypeInstance(c, m.bindings, ff.sym.info, ff)
result = generateTypeInstance(c, m.bindings, Concept.sym.info, ff)
else:
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 not containsOrIncl(marker, t.id):
case t.kind
of tyGenericInst, tyGenericBody, tyAlias:
of tyGenericInst, tyGenericBody, tyAlias, tyInferred:
result = iterOverTypeAux(marker, lastSon(t), iter, closure)
else:
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 a < maxAlign: a = maxAlign
result = align(result, a)
of tyInferred:
if typ.len > 1:
result = computeSizeAux(typ.lastSon, a)
of tyGenericInst, tyDistinct, tyGenericBody, tyAlias:
result = computeSizeAux(lastSon(typ), a)
of tyTypeClasses:
@@ -1351,18 +1354,17 @@ proc getSize(typ: PType): BiggestInt =
if result < 0: internalError("getSize: " & $typ.kind)
proc containsGenericTypeIter(t: PType, closure: RootRef): bool =
if t.kind == tyStatic:
case t.kind
of tyStatic:
return t.n == nil
if t.kind == tyTypeDesc:
of tyTypeDesc:
if t.base.kind == tyNone: return true
if containsGenericTypeIter(t.base, closure): return true
return false
if t.kind in GenericTypes + tyTypeClasses + {tyFromExpr}:
of GenericTypes + tyTypeClasses + {tyFromExpr}:
return true
return false
else:
return false
proc containsGenericType*(t: PType): bool =
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)
genLit(c, n, dest)
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:
if dest < 0: dest = c.getTemp(n.typ)
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
### matrixalgo.nim
import typetraits
type
AnyMatrix*[R, C: static[int]; T] = concept m, var mvar, type M
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
mvar[int, int] = T
type TransposedType = StripGenericParams(M)[C, R, 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 c in 0 .. <m.C:
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 setPerspectiveProjection*(m: AnyTransform3D) =
...
--------------
### matrix.nim
@@ -331,24 +341,28 @@ The concept types can be parametric just like the regular generic types:
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: 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
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
-------------
### usage.nim
import matrix, matrixalgo
var v: Matrix[3, 3, int]
echo v.determinant
var
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
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 """
output: "0\n0"
output: "0\n0\n0"
msg: '''
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 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:
echo "R=", R, " C=", C, " TE=", TE, " FF=", FF, " FC=", FC, " T=", T.name
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]
@@ -63,5 +76,6 @@ static:
no x is Matrix[3, 4, 9, 15, 20, int]
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