Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ parameter and result types, not just their source-level shape. Use

- `std/nre2` is added to replace deprecated NRE.

- `system.typeof` adds a new parameter `modifierMode` to specify how type modifiers are handled.

[//]: # "Changes:"

- `std/math` The `^` symbol now supports floating-point as exponent in addition to the Natural type.
Expand Down
4 changes: 3 additions & 1 deletion compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,9 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType

proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = semExprCheck(c, n, flags)
if result.typ == nil:
if result.typ == nil and efInTypeof in flags:
result.typ = c.voidType
elif result.typ == nil:
localError(c.config, n.info, errExprXHasNoType %
renderTree(result, {renderNoComments}))
result.typ = errorType(c)
Expand Down
11 changes: 1 addition & 10 deletions compiler/semmagic.nim
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,8 @@ proc semAddr(c: PContext; n: PNode): PNode =
result.typ = makePtrType(c, x.typ.skipTypes({tySink}))

proc semTypeOf(c: PContext; n: PNode): PNode =
var m = BiggestInt 1 # typeOfIter
if n.len == 3:
let mode = semConstExpr(c, n[2])
if mode.kind != nkIntLit:
localError(c.config, n.info, "typeof: cannot evaluate 'mode' parameter at compile-time")
else:
m = mode.intVal
let typExpr = semTypeOfImpl(c, n)
result = newNodeI(nkTypeOfExpr, n.info)
inc c.inTypeofContext
defer: dec c.inTypeofContext # compiles can raise an exception
let typExpr = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {})
result.add typExpr
if typExpr.typ.kind == tyFromExpr:
typExpr.typ.incl tfNonConstExpr
Expand Down
62 changes: 52 additions & 10 deletions compiler/semtypes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2063,6 +2063,57 @@ proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType =
result.rawAddSon(base)
result.incl tfHasStatic

proc semTypeOfImpl(c: PContext; n: PNode): PNode =
var m = BiggestInt 1 # typeOfIter
var modifierMode = BiggestInt 0 # typeOfModCompatible
type
TypeOfParams = enum
topMode
topModifier
if n.len in 3 .. 4:
for i in 2 ..< n.len:
var argKind = topMode
var arg: PNode = nil
if n[i].kind == nkExprEqExpr and n[i][0].kind == nkIdent:
# named param
case n[i][0].ident.s
of "mode": argKind = topMode
of "modifierMode": argKind = topModifier
else:
localError(c.config, n.info, "typeof: got unknown parameter name")
arg = n[i][1]
else:
if i == 2:
argKind = topMode
else:
argKind = topModifier
arg = n[i]
case argKind
of topMode:
let mode = semConstExpr(c, arg)
if mode.kind != nkIntLit:
localError(c.config, n.info, "typeof: cannot evaluate 'mode' parameter at compile-time")
else:
m = mode.intVal
of topModifier:
let modMode = semConstExpr(c, arg)
if modMode.kind != nkIntLit:
localError(c.config, n.info, "typeof: cannot evaluate 'modifierMode' parameter at compile-time")
else:
modifierMode = modMode.intVal

inc c.inTypeofContext
defer: dec c.inTypeofContext # compiles can raise an exception
var typExpr = semExprNoDeref(c, n[1], if m == 1: {efInTypeof} else: {})
if modifierMode == 0:
# typeOfModCompatible
typExpr.typ = typExpr.typ.skipTypes({tyVar, tyLent})
elif modifierMode == 1:
# typeOfModRemoveModifier
typExpr.typ = typExpr.typ.skipTypes({tyVar, tyLent, tySink})

result = typExpr

proc semTypeOf(c: PContext; n: PNode; prev: PType): PType =
openScope(c)
inc c.inTypeofContext
Expand All @@ -2083,16 +2134,7 @@ proc semTypeOf(c: PContext; n: PNode; prev: PType): PType =

proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType =
openScope(c)
var m = BiggestInt 1 # typeOfIter
if n.len == 3:
let mode = semConstExpr(c, n[2])
if mode.kind != nkIntLit:
localError(c.config, n.info, "typeof: cannot evaluate 'mode' parameter at compile-time")
else:
m = mode.intVal
inc c.inTypeofContext
defer: dec c.inTypeofContext # compiles can raise an exception
let ex = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {})
let ex = semTypeOfImpl(c, n)
closeScope(c)
result = ex.typ
if result.kind == tyFromExpr:
Expand Down
12 changes: 11 additions & 1 deletion lib/system.nim
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,12 @@ type
typeOfProc, ## Prefer the interpretation that means `x` is a proc call.
typeOfIter ## Prefer the interpretation that means `x` is an iterator call.

proc typeof*(x: untyped; mode = typeOfIter): typedesc {.
TypeOfModifierMode* = enum ## Modes to handle type modifiers `var`, `sink` and `lent`.
typeOfModCompatible, ## Remove or keep type modifiers in the same way as old typeof. That means keep `sink` but remove `var` and `lent`.
typeOfModRemoveModifier, ## Remove type modifiers.
typeOfModKeepModifier, ## Keep type modifiers.

proc typeof*(x: untyped; mode = typeOfIter; modifierMode = typeOfModCompatible): typedesc {.
magic: "TypeOf", noSideEffect, compileTime.} =
## Builtin `typeof` operation for accessing the type of an expression.
## Since version 0.20.0.
Expand All @@ -76,6 +81,11 @@ proc typeof*(x: untyped; mode = typeOfIter): typedesc {.
# since `typeOfProc` expects a typed expression and `myFoo2()` can
# only be used in a `for` context.

proc varParam(x: var int;
y: typeof(x, modifierMode = typeOfModRemoveModifier);
z: typeof(x, modifierMode = typeOfModKeepModifier)) = discard
doAssert varParam is proc (x: var int; y: int; z: var int) {.nimcall.}

proc `or`*(a, b: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.}
## Constructs an `or` meta class.

Expand Down
90 changes: 90 additions & 0 deletions tests/system/ttypeof.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
static: doAssert typeof(1) is int

func isVar[T](x: var T): bool = true
func isVar[T](x: T): bool = false

proc testVarParams1(a: var int;
b: typeof(a);
c: typeof(a, typeOfIter);
d: typeof(a, typeOfIter, typeOfModCompatible);
e: typeof(a, typeOfIter, typeOfModRemoveModifier);
f: typeof(a, typeOfIter, typeOfModKeepModifier);
g: typeof(a, modifierMode = typeOfModCompatible);
h: typeof(a, modifierMode = typeOfModRemoveModifier);
i: typeof(a, modifierMode = typeOfModKeepModifier);
) =
doAssert not isVar(b)
doAssert not isVar(c)
doAssert not isVar(d)
doAssert not isVar(e)
doAssert isVar(f)
doAssert not isVar(g)
doAssert not isVar(h)
doAssert isVar(i)

static: doAssert testVarParams1 is proc (a: var int; b: int; c: int; d: int; e: int; f: var int; g: int; h: int; i: var int) {.nimcall.}

block:
var a, f, i: int
testVarParams1(a, 0, 0, 0, 0, f, 0, 0, i)

# `typeOfModCompatible` and `typeOfModRemoveModifier` remove only top `var`, not `var` inside proc type
proc testVarParams2(a: var proc(x: var int): var int;
b: typeof(a);
c: typeof(a, modifierMode = typeOfModCompatible);
d: typeof(a, modifierMode = typeOfModRemoveModifier);
e: typeof(a, modifierMode = typeOfModKeepModifier)) =
doAssert not isVar(b)
doAssert not isVar(c)
doAssert not isVar(d)
doAssert isVar(e)

static: doAssert testVarParams2 is proc (a: var proc(x: var int): var int;
b: proc(x: var int): var int;
c: proc(x: var int): var int;
d: proc(x: var int): var int;
e: var proc(x: var int): var int) {.nimcall.}

block:
var a, e: proc(x: var int): var int = nil
let b, c, d: proc(x: var int): var int = nil
testVarParams2(a, b, c, d, e)

proc testRet(a: var int): typeof(a) = 0
static: doAssert testRet is proc (a: var int): int {.nimcall.}
proc testRet2(a: var int): typeof(a, modifierMode = typeOfModCompatible) = 0
static: doAssert testRet2 is proc (a: var int): int {.nimcall.}
proc testRet3(a: var int): typeof(a, modifierMode = typeOfModRemoveModifier) = 0
static: doAssert testRet3 is proc (a: var int): int {.nimcall.}

proc fooSink1(a: sink string;
b: typeof(a);
c: typeof(a, modifierMode = typeOfModCompatible);
d: typeof(a, modifierMode = typeOfModRemoveModifier);
e: typeof(a, modifierMode = typeOfModKeepModifier)) = discard

static: doAssert fooSink1 is proc (a: sink string; b: sink string; c: sink string; d: string; e: sink string) {.nimcall.}

proc fooLentRet(a: seq[string]): lent string = a[0]
proc testLentRetComp(a: seq[string]): typeof(fooLentRet(a), modifierMode = typeOfModCompatible) = a[0]
proc testLentRetRemo(a: seq[string]): typeof(fooLentRet(a), modifierMode = typeOfModRemoveModifier) = a[0]
proc testLentRetKeep(a: seq[string]): typeof(fooLentRet(a), modifierMode = typeOfModKeepModifier) = a[0]

# workaround # issue 25830
proc dummyLentProc(a: seq[string]): lent string = a[0]

static:
doAssert testLentRetComp is proc (a: seq[string]): string {.nimcall.}
doAssert testLentRetRemo is proc (a: seq[string]): string {.nimcall.}
doAssert testLentRetKeep is typeof(dummyLentProc)

proc voidProc() = discard
static:
doAssert typeof(voidProc()) is void

type
Foo = typeof(Bar)
Bar = int

static:
doAssert Foo is int
Loading