diff options
-rw-r--r-- | compiler/parser.nim | 3 | ||||
-rw-r--r-- | compiler/semdata.nim | 3 | ||||
-rw-r--r-- | compiler/semexprs.nim | 81 | ||||
-rw-r--r-- | compiler/semtypes.nim | 7 | ||||
-rw-r--r-- | tests/metatype/ttypedesc1.nim | 34 | ||||
-rw-r--r-- | tests/metatype/ttypedesc3.nim | 6 | ||||
-rw-r--r-- | tests/metatype/ttypeselectors.nim | 6 | ||||
-rw-r--r-- | tests/parser/ttypeclasses.nim | 39 | ||||
-rw-r--r-- | tests/statictypes/tstatictypes.nim | 84 | ||||
-rw-r--r-- | tests/types/tisopr.nim | 2 |
10 files changed, 210 insertions, 55 deletions
diff --git a/compiler/parser.nim b/compiler/parser.nim index 33ee8c9e6..5c99363c9 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -798,8 +798,7 @@ proc primarySuffix(p: var TParser, r: PNode, # `foo ref` or `foo ptr`. Unfortunately, these two are also # used as infix operators for the memory regions feature and # the current parsing rules don't play well here. - if mode == pmTypeDef or - (p.inPragma == 0 and (isUnary(p) or p.tok.tokType notin {tkOpr, tkDotDot})): + if p.inPragma == 0 and (isUnary(p) or p.tok.tokType notin {tkOpr, tkDotDot}): # actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet # solution, but pragmas.nim can't handle that let a = result diff --git a/compiler/semdata.nim b/compiler/semdata.nim index c858b6839..aa0cb6e8e 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -288,7 +288,8 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType = result.addSonSkipIntLit(typ) proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode = - let typedesc = makeTypeDesc(c, typ) + let typedesc = newTypeS(tyTypeDesc, c) + typedesc.addSonSkipIntLit(assertNotNil(c.config, typ)) let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info, c.config.options).linkTo(typedesc) return newSymNode(sym, info) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 4b45ccf87..a072deb7d 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -194,7 +194,7 @@ proc semConv(c: PContext, n: PNode): PNode = var targetType = semTypeNode(c, n.sons[0], nil) if targetType.kind == tyTypeDesc: - internalAssert targetType.len > 0 + internalAssert c.config, targetType.len > 0 if targetType.base.kind == tyNone: return semTypeOf(c, n[1]) else: @@ -316,57 +316,98 @@ proc semSizeof(c: PContext, n: PNode): PNode = n.typ = getSysType(c.graph, n.info, tyInt) result = n +proc fixupStaticType(c: PContext, n: PNode) = + # This proc can be applied to evaluated expressions to assign + # them a static type. + # + # XXX: with implicit static, this should not be necessary, + # because the output type of operations such as `semConstExpr` + # should be a static type (as well as the type of any other + # expression that can be implicitly evaluated). For now, we + # apply this measure only in code that is enlightened to work + # with static types. + if n.typ.kind != tyStatic: + n.typ = newTypeWithSons(getCurrOwner(c), tyStatic, @[n.typ]) + n.typ.n = n # XXX: cycles like the one here look dangerous. + # Consider using `n.copyTree` + proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode = - internalAssert c.config, n.sonsLen == 3 and - n[1].typ != nil and n[1].typ.kind == tyTypeDesc and + internalAssert c.config, + n.sonsLen == 3 and + n[1].typ != nil and n[2].kind in {nkStrLit..nkTripleStrLit, nkType} - let t1 = n[1].typ.skipTypes({tyTypeDesc}) + var + res = false + t1 = n[1].typ + t2 = n[2].typ + + if t1.kind == tyTypeDesc and t2.kind != tyTypeDesc: + t1 = t1.base if n[2].kind in {nkStrLit..nkTripleStrLit}: case n[2].strVal.normalize of "closure": let t = skipTypes(t1, abstractRange) - result = newIntNode(nkIntLit, ord(t.kind == tyProc and - t.callConv == ccClosure and - tfIterator notin t.flags)) + res = t.kind == tyProc and + t.callConv == ccClosure and + tfIterator notin t.flags else: - result = newIntNode(nkIntLit, 0) + res = false else: - var rhsOrigType = n[2].typ - var t2 = rhsOrigType.skipTypes({tyTypeDesc}) maybeLiftType(t2, c, n.info) var m: TCandidate initCandidate(c, m, t2) if efExplain in flags: m.diagnostics = @[] m.diagnosticsEnabled = true - let match = typeRel(m, t2, t1) >= isSubtype # isNone - result = newIntNode(nkIntLit, ord(match)) + res = typeRel(m, t2, t1) >= isSubtype # isNone + result = newIntNode(nkIntLit, ord(res)) result.typ = n.typ proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode = if sonsLen(n) != 3: localError(c.config, n.info, "'is' operator takes 2 arguments") + let boolType = getSysType(c.graph, n.info, tyBool) result = n - n.typ = getSysType(c.graph, n.info, tyBool) + n.typ = boolType + var liftLhs = true n.sons[1] = semExprWithType(c, n[1], {efDetermineType, efWantIterator}) if n[2].kind notin {nkStrLit..nkTripleStrLit}: let t2 = semTypeNode(c, n[2], nil) n.sons[2] = newNodeIT(nkType, n[2].info, t2) + if t2.kind == tyStatic: + let evaluated = tryConstExpr(c, n[1]) + if evaluated != nil: + c.fixupStaticType(evaluated) + n[1] = evaluated + else: + result = newIntNode(nkIntLit, 0) + result.typ = boolType + return + elif t2.kind == tyTypeDesc and + (t2.base.kind == tyNone or tfExplicit in t2.flags): + # When the right-hand side is an explicit type, we must + # not allow regular values to be matched against the type: + liftLhs = false - let lhsType = n[1].typ + var lhsType = n[1].typ if lhsType.kind != tyTypeDesc: - n.sons[1] = makeTypeSymNode(c, lhsType, n[1].info) - elif lhsType.base.kind == tyNone: - # this is a typedesc variable, leave for evals - return + if liftLhs: + n[1] = makeTypeSymNode(c, lhsType, n[1].info) + lhsType = n[1].typ + else: + if lhsType.base.kind == tyNone: + # this is a typedesc variable, leave for evals + return + if lhsType.base.containsGenericType: + # BUGFIX: don't evaluate this too early: ``T is void`` + return - # BUGFIX: don't evaluate this too early: ``T is void`` - if not n[1].typ.base.containsGenericType: result = isOpImpl(c, n, flags) + result = isOpImpl(c, n, flags) proc semOpAux(c: PContext, n: PNode) = const flags = {efDetermineType} diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 6b27c8032..64783f890 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1472,8 +1472,11 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of mSeq: result = semContainer(c, n, tySequence, "seq", prev) of mOpt: result = semContainer(c, n, tyOpt, "opt", prev) of mVarargs: result = semVarargs(c, n, prev) - of mTypeDesc, mTypeTy: result = makeTypeDesc(c, semTypeNode(c, n[1], nil)) - of mStaticTy: result = semStaticType(c, n[1], prev) + of mTypeDesc, mTypeTy: + result = makeTypeDesc(c, semTypeNode(c, n[1], nil)) + result.flags.incl tfExplicit + of mStaticTy: + result = semStaticType(c, n[1], prev) of mExpr: result = semTypeNode(c, n.sons[0], nil) if result != nil: diff --git a/tests/metatype/ttypedesc1.nim b/tests/metatype/ttypedesc1.nim index e9eee581f..837c8eccc 100644 --- a/tests/metatype/ttypedesc1.nim +++ b/tests/metatype/ttypedesc1.nim @@ -5,15 +5,16 @@ type x: T y: U -proc getTypeName(t: typedesc): string = t.name +proc getTypeName1(t: typedesc): string = t.name +proc getTypeName2(t: type): string = t.name -proc foo(T: typedesc[float], a: auto): string = +proc foo(T: type float, a: auto): string = result = "float " & $(a.len > 5) proc foo(T: typedesc[TFoo], a: int): string = result = "TFoo " & $(a) -proc foo(T: typedesc[int or bool]): string = +proc foo(T: type[int or bool]): string = var a: T a = 10 result = "int or bool " & ($a) @@ -23,8 +24,8 @@ template foo(T: typedesc[seq]): string = "seq" test "types can be used as proc params": # XXX: `check` needs to know that TFoo[int, float] is a type and # cannot be assigned for a local variable for later inspection - check ((string.getTypeName == "string")) - check ((getTypeName(int) == "int")) + check ((string.getTypeName1 == "string")) + check ((getTypeName2(int) == "int")) check ((foo(TFoo[int, float], 1000) == "TFoo 1000")) @@ -37,6 +38,25 @@ test "types can be used as proc params": check ((foo(seq[int]) == "seq")) check ((foo(seq[TFoo[bool, string]]) == "seq")) -when false: - proc foo(T: typedesc[seq], s: T) = nil +template accept(x) = + static: assert(compiles(x)) + +template reject(x) = + static: assert(not compiles(x)) + +var + si: seq[int] + ss: seq[string] + +proc foo(T: typedesc[seq], s: T) = + discard + +accept: + foo seq[int], si + +reject: + foo seq[string], si + +reject: + foo seq[int], ss diff --git a/tests/metatype/ttypedesc3.nim b/tests/metatype/ttypedesc3.nim index 9f19bd6e3..3d1cf2ec9 100644 --- a/tests/metatype/ttypedesc3.nim +++ b/tests/metatype/ttypedesc3.nim @@ -4,9 +4,9 @@ type Base = object of RootObj Child = object of Base -proc pr(T: typedesc[Base]) = echo "proc " & T.name -method me(T: typedesc[Base]) = echo "method " & T.name -iterator it(T: typedesc[Base]): auto = yield "yield " & T.name +proc pr(T: type[Base]) = echo "proc " & T.name +method me(T: type[Base]) = echo "method " & T.name +iterator it(T: type[Base]): auto = yield "yield " & T.name Base.pr Child.pr diff --git a/tests/metatype/ttypeselectors.nim b/tests/metatype/ttypeselectors.nim index 1209fe78f..2a2455adb 100644 --- a/tests/metatype/ttypeselectors.nim +++ b/tests/metatype/ttypeselectors.nim @@ -5,16 +5,16 @@ output: "8\n8\n4" import macros, typetraits -template selectType(x: int): typeDesc = +template selectType(x: int): type = when x < 10: int else: string -template simpleTypeTempl: typeDesc = +template simpleTypeTempl: type = string -macro typeFromMacro: typedesc = string +macro typeFromMacro: type = string # The tests below check that the result variable of the # selected type matches the literal types in the code: diff --git a/tests/parser/ttypeclasses.nim b/tests/parser/ttypeclasses.nim index 9f487c7a8..46fd20686 100644 --- a/tests/parser/ttypeclasses.nim +++ b/tests/parser/ttypeclasses.nim @@ -1,17 +1,42 @@ -discard """ - action: run -""" - type R = ref V = var D = distinct P = ptr + T = type + S = static + OBJ = object + TPL = tuple + SEQ = seq +var i: int var x: ref int var y: distinct int var z: ptr int +const C = @[1, 2, 3] + +static: + assert x is ref + assert y is distinct + assert z is ptr + assert C is static + assert C[1] is static[int] + assert C[0] is static[SomeInteger] + assert C isnot static[string] + assert C is SEQ|OBJ + assert C isnot OBJ|TPL + assert int is int + assert int is T + assert int is SomeInteger + assert seq[int] is type + assert seq[int] is type[seq] + assert seq[int] isnot type[seq[float]] + assert i isnot type[int] + assert type(i) is type[int] + assert x isnot T + assert y isnot S + assert z isnot enum + assert x isnot object + assert y isnot tuple + assert z isnot seq -doAssert x is ref -doAssert y is distinct -doAssert z is ptr \ No newline at end of file diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim index 5234866fa..789bd7588 100644 --- a/tests/statictypes/tstatictypes.nim +++ b/tests/statictypes/tstatictypes.nim @@ -1,25 +1,91 @@ discard """ nimout: ''' -staticAlialProc instantiated with 4 -staticAlialProc instantiated with 6 +staticAlialProc instantiated with 358 +staticAlialProc instantiated with 368 +''' +output: ''' +16 +16 +b is 2 times a +17 ''' """ import macros +template ok(x) = assert(x) +template no(x) = assert(not x) + +template accept(x) = + static: assert(compiles(x)) + +template reject(x) = + static: assert(not compiles(x)) + proc plus(a, b: int): int = a + b +template isStatic(x: static): bool = true +template isStatic(x: auto): bool = false + +var v = 1 + when true: + # test that `isStatic` works as expected + const C = 2 + + static: + ok C.isStatic + ok isStatic(plus(1, 2)) + ok plus(C, 2).isStatic + + no isStatic(v) + no plus(1, v).isStatic + +when true: + # test that proc instantiation works as expected type StaticTypeAlias = static[int] - proc staticAliasProc(s: StaticTypeAlias) = - static: echo "staticAlialProc instantiated with ", s + 1 - echo s + proc staticAliasProc(a: StaticTypeAlias, + b: static[int], + c: static int) = + static: + assert a.isStatic and b.isStatic and c.isStatic + assert isStatic(a + plus(b, c)) + echo "staticAlialProc instantiated with ", a, b, c + + when b mod a == 0: + echo "b is ", b div a, " times a" + + echo a + b + c - staticAliasProc 1+2 - staticAliasProc 3 - staticAliasProc 5 + staticAliasProc 1+2, 5, 8 + staticAliasProc 3, 2+3, 9-1 + staticAliasProc 3, 3+3, 4+4 + +when true: + # test static coercions. normal cases that should work: + accept: + var s1 = static[int] plus(1, 2) + var s2 = static(plus(1,2)) + var s3 = static plus(1,2) + var s4 = static[SomeInteger](1 + 2) + + # the sub-script operator can be used only with types: + reject: + var just_static3 = static[plus(1,2)] + + # static coercion takes into account the type: + reject: + var x = static[string](plus(1, 2)) + reject: + var x = static[string] plus(1, 2) + reject: + var x = static[SomeFloat] plus(3, 4) + + # you cannot coerce a run-time variable + reject: + var x = static(v) when true: type @@ -35,7 +101,7 @@ when true: var aw1: ArrayWrapper1[5] var aw2: ArrayWrapper2[5] var aw3: ArrayWrapper3[(10, "str")] - + static: assert aw1.data.high == 5 assert aw2.data.high == 6 diff --git a/tests/types/tisopr.nim b/tests/types/tisopr.nim index 2f9dbf245..f05f443de 100644 --- a/tests/types/tisopr.nim +++ b/tests/types/tisopr.nim @@ -5,7 +5,7 @@ false false true true -no''' +yes''' """ proc IsVoid[T](): string = |