mirror of
https://github.com/nim-lang/Nim.git
synced 2026-06-14 15:43:45 +00:00
adds modifierMode parameter to typeof (#25815)
This PR adds 3 modes to `typeof` to specify how 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.
Related to https://github.com/nim-lang/Nim/pull/25779
https://github.com/nim-lang/Nim/issues/25786
(cherry picked from commit 48621c217f)
This commit is contained in:
@@ -70,6 +70,10 @@ parameter and result types, not just their source-level shape. Use
|
||||
Modes include `Nim` (default, fully compatible) and two new experimental modes:
|
||||
`Lax` and `Gnu` for different option parsing behaviors.
|
||||
|
||||
- `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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.flags.incl tfNonConstExpr
|
||||
|
||||
@@ -1949,6 +1949,57 @@ proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType =
|
||||
result.rawAddSon(base)
|
||||
result.flags.incl tfHasStatic
|
||||
|
||||
proc semTypeOfImpl(c: PContext; n: PNode): PNode =
|
||||
var m = BiggestInt 1 # typeOfIter
|
||||
var modifierMode = BiggestInt 0 # CompatibleTypeModifiers
|
||||
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:
|
||||
# CompatibleTypeModifiers
|
||||
typExpr.typ = typExpr.typ.skipTypes({tyVar, tyLent})
|
||||
elif modifierMode == 1:
|
||||
# RemoveTypeModifiers
|
||||
typExpr.typ = typExpr.typ.skipTypes({tyVar, tyLent, tySink})
|
||||
|
||||
result = typExpr
|
||||
|
||||
proc semTypeOf(c: PContext; n: PNode; prev: PType): PType =
|
||||
openScope(c)
|
||||
inc c.inTypeofContext
|
||||
@@ -1969,16 +2020,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:
|
||||
|
||||
@@ -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 {.
|
||||
TypeOfModifiers* = enum ## Modes to handle type modifiers `var`, `sink` and `lent`.
|
||||
CompatibleTypeModifiers, ## Remove or keep type modifiers in the same way as old typeof. That means keep `sink` but remove `var` and `lent`.
|
||||
RemoveTypeModifiers, ## Remove type modifiers.
|
||||
KeepTypeModifiers, ## Keep type modifiers.
|
||||
|
||||
proc typeof*(x: untyped; mode = typeOfIter; modifierMode = CompatibleTypeModifiers): typedesc {.
|
||||
magic: "TypeOf", noSideEffect, compileTime.} =
|
||||
## Builtin `typeof` operation for accessing the type of an expression.
|
||||
## Since version 0.20.0.
|
||||
@@ -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 = RemoveTypeModifiers);
|
||||
z: typeof(x, modifierMode = KeepTypeModifiers)) = 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.
|
||||
|
||||
|
||||
90
tests/system/ttypeof.nim
Normal file
90
tests/system/ttypeof.nim
Normal file
@@ -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, CompatibleTypeModifiers);
|
||||
e: typeof(a, typeOfIter, RemoveTypeModifiers);
|
||||
f: typeof(a, typeOfIter, KeepTypeModifiers);
|
||||
g: typeof(a, modifierMode = CompatibleTypeModifiers);
|
||||
h: typeof(a, modifierMode = RemoveTypeModifiers);
|
||||
i: typeof(a, modifierMode = KeepTypeModifiers);
|
||||
) =
|
||||
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)
|
||||
|
||||
# `CompatibleTypeModifiers` and `RemoveTypeModifiers` 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 = CompatibleTypeModifiers);
|
||||
d: typeof(a, modifierMode = RemoveTypeModifiers);
|
||||
e: typeof(a, modifierMode = KeepTypeModifiers)) =
|
||||
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 = CompatibleTypeModifiers) = 0
|
||||
static: doAssert testRet2 is proc (a: var int): int {.nimcall.}
|
||||
proc testRet3(a: var int): typeof(a, modifierMode = RemoveTypeModifiers) = 0
|
||||
static: doAssert testRet3 is proc (a: var int): int {.nimcall.}
|
||||
|
||||
proc fooSink1(a: sink string;
|
||||
b: typeof(a);
|
||||
c: typeof(a, modifierMode = CompatibleTypeModifiers);
|
||||
d: typeof(a, modifierMode = RemoveTypeModifiers);
|
||||
e: typeof(a, modifierMode = KeepTypeModifiers)) = 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 = CompatibleTypeModifiers) = a[0]
|
||||
proc testLentRetRemo(a: seq[string]): typeof(fooLentRet(a), modifierMode = RemoveTypeModifiers) = a[0]
|
||||
proc testLentRetKeep(a: seq[string]): typeof(fooLentRet(a), modifierMode = KeepTypeModifiers) = 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
|
||||
Reference in New Issue
Block a user