mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-20 22:35:24 +00:00
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:
@@ -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]):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
15
tests/concepts/matrix.nim
Normal 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
|
||||
|
||||
28
tests/concepts/matrixalgo.nim
Normal file
28
tests/concepts/matrixalgo.nim
Normal 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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
31
tests/concepts/tmatrixlib.nim
Normal file
31
tests/concepts/tmatrixlib.nim
Normal 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
|
||||
|
||||
29
tests/generics/tgenericdotrettype.nim
Normal file
29
tests/generics/tgenericdotrettype.nim
Normal 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
|
||||
|
||||
Reference in New Issue
Block a user