diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index c51d406ac..05b2d8f9c 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -235,6 +235,13 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult of nkStmtList, nkStmtListExpr: if n.typ != nil: result = isAssignable(owner, n.lastSon, isUnsafeAddr) + of nkVarTy: + # XXX: The fact that this is here is a bit of a hack. + # The goal is to allow the use of checks such as "foo(var T)" + # within concepts. Semantically, it's not correct to say that + # nkVarTy denotes an lvalue, but the example above is the only + # possible code which will get us here + result = arLValue else: discard diff --git a/compiler/parser.nim b/compiler/parser.nim index 362a5c286..b63bab781 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -1793,8 +1793,16 @@ proc parseObject(p: var TParser): PNode = addSon(result, parseObjectPart(p)) proc parseTypeClassParam(p: var TParser): PNode = - if p.tok.tokType in {tkOut, tkVar}: - result = newNodeP(nkVarTy, p) + let modifier = case p.tok.tokType + of tkOut, tkVar: nkVarTy + of tkPtr: nkPtrTy + of tkRef: nkRefTy + of tkStatic: nkStaticTy + of tkType: nkTypeOfExpr + else: nkEmpty + + if modifier != nkEmpty: + result = newNodeP(modifier, p) getTok(p) result.addSon(p.parseSymbol) else: diff --git a/compiler/semdata.nim b/compiler/semdata.nim index ef23e40f2..dcd1e04b4 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -230,6 +230,17 @@ proc makePtrType*(c: PContext, baseType: PType): PType = result = newTypeS(tyPtr, c) addSonSkipIntLit(result, baseType.assertNotNil) +proc makeTypeWithModifier*(c: PContext, + modifier: TTypeKind, + baseType: PType): PType = + assert modifier in {tyVar, tyPtr, tyRef, tyStatic, tyTypeDesc} + + if modifier in {tyVar, tyTypeDesc} and baseType.kind == modifier: + result = baseType + else: + result = newTypeS(modifier, c) + addSonSkipIntLit(result, baseType.assertNotNil) + proc makeVarType*(c: PContext, baseType: PType): PType = if baseType.kind == tyVar: result = baseType @@ -238,8 +249,11 @@ proc makeVarType*(c: PContext, baseType: PType): PType = addSonSkipIntLit(result, baseType.assertNotNil) proc makeTypeDesc*(c: PContext, typ: PType): PType = - result = newTypeS(tyTypeDesc, c) - result.addSonSkipIntLit(typ.assertNotNil) + if typ.kind == tyTypeDesc: + result = typ + else: + result = newTypeS(tyTypeDesc, c) + result.addSonSkipIntLit(typ.assertNotNil) proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode = let typedesc = makeTypeDesc(c, typ) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index e3c3123f9..bf6c24310 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -135,7 +135,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType = let isCall = ord(n.kind in nkCallKinds+{nkBracketExpr}) let n = if n[0].kind == nkBracket: n[0] else: n checkMinSonsLen(n, 1) - var base = semTypeNode(c, n.lastSon, nil) + var base = semTypeNode(c, n.lastSon, nil).skipTypes({tyTypeDesc}) result = newOrPrevType(kind, prev, c) var isNilable = false # check every except the last is an object: @@ -155,7 +155,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType = proc semVarType(c: PContext, n: PNode, prev: PType): PType = if sonsLen(n) == 1: result = newOrPrevType(tyVar, prev, c) - var base = semTypeNode(c, n.sons[0], nil) + var base = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc}) if base.kind == tyVar: localError(n.info, errVarVarTypeNotAllowed) base = base.sons[0] @@ -1404,7 +1404,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of nkDistinctTy: result = semDistinct(c, n, prev) of nkStaticTy: result = newOrPrevType(tyStatic, prev, c) - var base = semTypeNode(c, n.sons[0], nil) + var base = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc}) result.rawAddSon(base) result.flags.incl tfHasStatic of nkIteratorTy: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 3dede8b6f..505e785d6 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -622,17 +622,27 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, dummyName: PNode dummyType: PType - if param.kind == nkVarTy: + let modifier = case param.kind + of nkVarTy: tyVar + of nkRefTy: tyRef + of nkPtrTy: tyPtr + of nkStaticTy: tyStatic + of nkTypeOfExpr: tyTypeDesc + else: tyNone + + if modifier != tyNone: dummyName = param[0] - dummyType = if a.kind != tyVar: makeVarType(c, a) else: a + dummyType = c.makeTypeWithModifier(modifier, a) else: dummyName = param dummyType = a internalAssert dummyName.kind == nkIdent - var dummyParam = newSym(skVar, dummyName.ident, body.sym, body.sym.info) + var dummyParam = newSym(if modifier == tyTypeDesc: skType else: skVar, + dummyName.ident, body.sym, body.sym.info) dummyParam.typ = dummyType addDecl(c, dummyParam) + #echo "B ", dummyName.ident.s, " ", typeToString(dummyType), " ", dummyparam.kind var checkedBody = c.semTryExpr(c, body.n[3].copyTree) @@ -1411,12 +1421,12 @@ proc inferTypeClassParam*(m: var TCandidate, f, a: PType): bool = if inferrableType == nil: return false var inferAs = f - + case f.kind of tyGenericParam: var prev = PType(idTableGet(m.bindings, f)) if prev != nil: inferAs = prev - + of tyFromExpr: let computedType = tryResolvingStaticExpr(m, f.n).typ case computedType.kind @@ -1426,10 +1436,10 @@ proc inferTypeClassParam*(m: var TCandidate, f, a: PType): bool = inferAs = computedType else: localError(f.n.info, errTypeExpected) - + else: discard - + inferrableType.assignType inferAs return true @@ -1470,7 +1480,8 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, argType = arg.typ var - useTypeLoweringRuleInTypeClass = c.inTypeClass > 0 and + useTypeLoweringRuleInTypeClass = argType != nil and + c.inTypeClass > 0 and not m.isNoCall and f.kind != tyTypeDesc @@ -1478,9 +1489,9 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, argType.skipTypes({tyTypeDesc, tyFieldAccessor}) else: argType - + r = typeRel(m, f, a) - + if r != isNone and m.calleeSym != nil and m.calleeSym.kind in {skMacro, skTemplate}: # XXX: duplicating this is ugly, but we cannot (!) move this diff --git a/tests/concepts/tusertypeclasses.nim b/tests/concepts/tusertypeclasses.nim index 612556949..9a20bfae5 100644 --- a/tests/concepts/tusertypeclasses.nim +++ b/tests/concepts/tusertypeclasses.nim @@ -2,16 +2,13 @@ discard """ output: '''Sortable Sortable Container -true -true -false -false -false ''' """ import typetraits +template reject(expr) = assert(not compiles(x)) + type TObj = object x: int @@ -36,33 +33,52 @@ foo(@[TObj(x: 10), TObj(x: 20)]) proc intval(x: int): int = 10 -# check real and virtual fields type - TFoo = concept T - T.x - y(T) + TFoo = concept o, type T, ref r, var v, ptr p, static s + o.x + y(o) is int + + var str: string + var intref: ref int + + refproc(ref T, ref int) + varproc(var T) + ptrproc(ptr T, str) + + staticproc(static[T]) + + typeproc o.type + o.type.typeproc + + refproc(r, intref) + varproc(v) + p.ptrproc(string) + staticproc s + typeproc(T) + + const TypeName = T.name + type MappedType = type(T.y) + intval T.y - let z = intval(T.y) + let z = intval(o.y) + + static: + assert T.name.len == 4 + reject o.name + reject o.typeproc + reject staticproc(o) + reject o.varproc + reject T.staticproc + reject p.staticproc proc y(x: TObj): int = 10 +proc varproc(x: var TObj) = discard +proc refproc(x: ref TObj, y: ref int) = discard +proc ptrproc(x: ptr TObj, y: string) = discard +proc staticproc(x: static[TObj]) = discard +proc typeproc(t: type TObj) = discard + proc testFoo(x: TFoo) = discard testFoo(TObj(x: 10)) -type - Matrix[Rows, Cols: static[int]; T] = concept M - M.M == Rows - M.N == Cols - M.T is T - - MyMatrix[M, N: static[int]; T] = object - data: array[M*N, T] - -var x: MyMatrix[3, 3, int] - -echo x is Matrix -echo x is Matrix[3, 3, int] -echo x is Matrix[3, 3, float] -echo x is Matrix[4, 3, int] -echo x is Matrix[3, 4, int] -