templates can access hidden fields
This commit is contained in:
parent
4d01408a46
commit
9e92455a53
6 changed files with 61 additions and 42 deletions
|
|
@ -146,29 +146,18 @@ proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
|
||||||
allowed: TSymFlags): PSym
|
allowed: TSymFlags): PSym
|
||||||
proc semStmtScope(c: PContext, n: PNode): PNode
|
proc semStmtScope(c: PContext, n: PNode): PNode
|
||||||
|
|
||||||
proc ParamsTypeCheck(c: PContext, typ: PType) {.inline.} =
|
proc paramsTypeCheck(c: PContext, typ: PType) {.inline.} =
|
||||||
if not typeAllowed(typ, skConst):
|
if not typeAllowed(typ, skConst):
|
||||||
LocalError(typ.n.info, errXisNoType, typeToString(typ))
|
localError(typ.n.info, errXisNoType, typeToString(typ))
|
||||||
|
|
||||||
proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym
|
proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym
|
||||||
proc semTemplateExpr(c: PContext, n: PNode, s: PSym, semCheck = true): PNode
|
proc semTemplateExpr(c: PContext, n: PNode, s: PSym, semCheck = true): PNode
|
||||||
proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode
|
proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode
|
||||||
proc semWhen(c: PContext, n: PNode, semCheck: bool = true): PNode
|
proc semWhen(c: PContext, n: PNode, semCheck: bool = true): PNode
|
||||||
proc IsOpImpl(c: PContext, n: PNode): PNode
|
proc isOpImpl(c: PContext, n: PNode): PNode
|
||||||
proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
|
proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
|
||||||
semCheck: bool = true): PNode
|
semCheck: bool = true): PNode
|
||||||
|
|
||||||
when false:
|
|
||||||
proc symFromType(t: PType, info: TLineInfo): PSym =
|
|
||||||
if t.sym != nil: return t.sym
|
|
||||||
result = newSym(skType, getIdent"AnonType", t.owner, info)
|
|
||||||
result.flags.incl sfAnon
|
|
||||||
result.typ = t
|
|
||||||
|
|
||||||
proc symNodeFromType(c: PContext, t: PType, info: TLineInfo): PNode =
|
|
||||||
result = newSymNode(symFromType(t, info), info)
|
|
||||||
result.typ = makeTypeDesc(c, t)
|
|
||||||
|
|
||||||
when false:
|
when false:
|
||||||
proc createEvalContext(c: PContext, mode: TEvalMode): PEvalContext =
|
proc createEvalContext(c: PContext, mode: TEvalMode): PEvalContext =
|
||||||
result = newEvalContext(c.module, mode)
|
result = newEvalContext(c.module, mode)
|
||||||
|
|
@ -184,12 +173,6 @@ when false:
|
||||||
result.handleIsOperator = proc (n: PNode): PNode =
|
result.handleIsOperator = proc (n: PNode): PNode =
|
||||||
result = IsOpImpl(c, n)
|
result = IsOpImpl(c, n)
|
||||||
|
|
||||||
proc evalConstExpr(c: PContext, module: PSym, e: PNode): PNode =
|
|
||||||
result = evalConstExprAux(c.createEvalContext(emConst), module, nil, e)
|
|
||||||
|
|
||||||
proc evalStaticExpr(c: PContext, module: PSym, e: PNode, prc: PSym): PNode =
|
|
||||||
result = evalConstExprAux(c.createEvalContext(emStatic), module, prc, e)
|
|
||||||
|
|
||||||
proc semConstExpr(c: PContext, n: PNode): PNode =
|
proc semConstExpr(c: PContext, n: PNode): PNode =
|
||||||
var e = semExprWithType(c, n)
|
var e = semExprWithType(c, n)
|
||||||
if e == nil:
|
if e == nil:
|
||||||
|
|
@ -226,7 +209,9 @@ include hlo, seminst, semcall
|
||||||
proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode =
|
proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode =
|
||||||
inc(evalTemplateCounter)
|
inc(evalTemplateCounter)
|
||||||
if evalTemplateCounter > 100:
|
if evalTemplateCounter > 100:
|
||||||
GlobalError(s.info, errTemplateInstantiationTooNested)
|
globalError(s.info, errTemplateInstantiationTooNested)
|
||||||
|
let oldFriend = c.friendModule
|
||||||
|
c.friendModule = s.owner.getModule
|
||||||
|
|
||||||
result = n
|
result = n
|
||||||
if s.typ.sons[0] == nil:
|
if s.typ.sons[0] == nil:
|
||||||
|
|
@ -250,6 +235,7 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode =
|
||||||
result = fitNode(c, s.typ.sons[0], result)
|
result = fitNode(c, s.typ.sons[0], result)
|
||||||
#GlobalError(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0]))
|
#GlobalError(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0]))
|
||||||
dec(evalTemplateCounter)
|
dec(evalTemplateCounter)
|
||||||
|
c.friendModule = oldFriend
|
||||||
|
|
||||||
proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
|
proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
|
||||||
semCheck: bool = true): PNode =
|
semCheck: bool = true): PNode =
|
||||||
|
|
@ -316,7 +302,7 @@ proc myOpenCached(module: PSym, rd: PRodReader): PPassContext =
|
||||||
result = myOpen(module)
|
result = myOpen(module)
|
||||||
for m in items(rd.methods): methodDef(m, true)
|
for m in items(rd.methods): methodDef(m, true)
|
||||||
|
|
||||||
proc SemStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
|
proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
|
||||||
result = semStmt(c, n)
|
result = semStmt(c, n)
|
||||||
# BUGFIX: process newly generated generics here, not at the end!
|
# BUGFIX: process newly generated generics here, not at the end!
|
||||||
if c.lastGenericIdx < c.generics.len:
|
if c.lastGenericIdx < c.generics.len:
|
||||||
|
|
@ -331,7 +317,7 @@ proc SemStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
|
||||||
result = buildEchoStmt(c, result)
|
result = buildEchoStmt(c, result)
|
||||||
result = transformStmt(c.module, result)
|
result = transformStmt(c.module, result)
|
||||||
|
|
||||||
proc RecoverContext(c: PContext) =
|
proc recoverContext(c: PContext) =
|
||||||
# clean up in case of a semantic error: We clean up the stacks, etc. This is
|
# clean up in case of a semantic error: We clean up the stacks, etc. This is
|
||||||
# faster than wrapping every stack operation in a 'try finally' block and
|
# faster than wrapping every stack operation in a 'try finally' block and
|
||||||
# requires far less code.
|
# requires far less code.
|
||||||
|
|
|
||||||
|
|
@ -1268,12 +1268,13 @@ exclude ``nil`` as a valid value with the `not nil`:idx: annotation:
|
||||||
# compiler catches this:
|
# compiler catches this:
|
||||||
p(nil)
|
p(nil)
|
||||||
|
|
||||||
# but not this:
|
# and also this:
|
||||||
var x: PObject
|
var x: PObject
|
||||||
p(x)
|
p(x)
|
||||||
|
|
||||||
As shown in the example this is merely an annotation for documentation purposes;
|
The compiler ensures that every code path initializes variables which contain
|
||||||
for now the compiler can only catch the most trivial type violations.
|
not nilable pointers. The details of this analysis are still to be specified
|
||||||
|
here.
|
||||||
|
|
||||||
|
|
||||||
Procedural type
|
Procedural type
|
||||||
|
|
@ -1504,8 +1505,8 @@ The ``void`` type is particularly useful for generic code:
|
||||||
else:
|
else:
|
||||||
p(x)
|
p(x)
|
||||||
|
|
||||||
proc intProc(x: int) = nil
|
proc intProc(x: int) = discard
|
||||||
proc emptyProc() = nil
|
proc emptyProc() = discard
|
||||||
|
|
||||||
callProc[int](intProc, 12)
|
callProc[int](intProc, 12)
|
||||||
callProc[void](emptyProc)
|
callProc[void](emptyProc)
|
||||||
|
|
@ -1767,6 +1768,15 @@ been declared with the `discardable`:idx: pragma:
|
||||||
|
|
||||||
p(3, 4) # now valid
|
p(3, 4) # now valid
|
||||||
|
|
||||||
|
An empty ``discard`` statement is often used as a null statement:
|
||||||
|
|
||||||
|
.. code-block:: nimrod
|
||||||
|
proc classify(s: string) =
|
||||||
|
case s[0]
|
||||||
|
of SymChars, '_': echo "an identifier"
|
||||||
|
of '0'..'9': echo "a number"
|
||||||
|
else: discard
|
||||||
|
|
||||||
|
|
||||||
Var statement
|
Var statement
|
||||||
-------------
|
-------------
|
||||||
|
|
@ -1816,7 +1826,7 @@ If a proc is annotated with the ``noinit`` pragma this refers to its implicit
|
||||||
``result`` variable:
|
``result`` variable:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nimrod
|
||||||
proc returnUndefinedValue: int {.noinit.} = nil
|
proc returnUndefinedValue: int {.noinit.} = discard
|
||||||
|
|
||||||
|
|
||||||
The implicit initialization can be also prevented by the `requiresInit`:idx:
|
The implicit initialization can be also prevented by the `requiresInit`:idx:
|
||||||
|
|
@ -3207,7 +3217,7 @@ Nimrod also allows for type classes and regular types to be specified
|
||||||
as `type constraints`:idx: of the generic type parameter:
|
as `type constraints`:idx: of the generic type parameter:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nimrod
|
||||||
proc onlyIntOrString[T: int|string](x, y: T) = nil
|
proc onlyIntOrString[T: int|string](x, y: T) = discard
|
||||||
|
|
||||||
onlyIntOrString(450, 616) # valid
|
onlyIntOrString(450, 616) # valid
|
||||||
onlyIntOrString(5.0, 0.0) # type mismatch
|
onlyIntOrString(5.0, 0.0) # type mismatch
|
||||||
|
|
@ -3782,7 +3792,7 @@ regular expressions:
|
||||||
macro case_token(n: stmt): stmt =
|
macro case_token(n: stmt): stmt =
|
||||||
# creates a lexical analyzer from regular expressions
|
# creates a lexical analyzer from regular expressions
|
||||||
# ... (implementation is an exercise for the reader :-)
|
# ... (implementation is an exercise for the reader :-)
|
||||||
nil
|
discard
|
||||||
|
|
||||||
case_token: # this colon tells the parser it is a macro statement
|
case_token: # this colon tells the parser it is a macro statement
|
||||||
of r"[A-Za-z_]+[A-Za-z_0-9]*":
|
of r"[A-Za-z_]+[A-Za-z_0-9]*":
|
||||||
|
|
@ -3811,17 +3821,17 @@ Whole routines (procs, iterators etc.) can also be passed to a template or
|
||||||
a macro via the pragma notation:
|
a macro via the pragma notation:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nimrod
|
||||||
template m(s: stmt) = nil
|
template m(s: stmt) = discard
|
||||||
|
|
||||||
proc p() {.m.} = nil
|
proc p() {.m.} = discard
|
||||||
|
|
||||||
This is a simple syntactic transformation into:
|
This is a simple syntactic transformation into:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nimrod
|
||||||
template m(s: stmt) = nil
|
template m(s: stmt) = discard
|
||||||
|
|
||||||
m:
|
m:
|
||||||
proc p() = nil
|
proc p() = discard
|
||||||
|
|
||||||
|
|
||||||
Special Types
|
Special Types
|
||||||
|
|
@ -4142,9 +4152,9 @@ all the arguments, but also the matched operators in reverse polish notation:
|
||||||
TMatrix = object
|
TMatrix = object
|
||||||
dummy: int
|
dummy: int
|
||||||
|
|
||||||
proc `*`(a, b: TMatrix): TMatrix = nil
|
proc `*`(a, b: TMatrix): TMatrix = discard
|
||||||
proc `+`(a, b: TMatrix): TMatrix = nil
|
proc `+`(a, b: TMatrix): TMatrix = discard
|
||||||
proc `-`(a, b: TMatrix): TMatrix = nil
|
proc `-`(a, b: TMatrix): TMatrix = discard
|
||||||
proc `$`(a: TMatrix): string = result = $a.dummy
|
proc `$`(a: TMatrix): string = result = $a.dummy
|
||||||
proc mat21(): TMatrix =
|
proc mat21(): TMatrix =
|
||||||
result.dummy = 21
|
result.dummy = 21
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ proc write(f: TFile, b: bool) =
|
||||||
proc write(f: TFile, r: float32) = fprintf(f, "%g", r)
|
proc write(f: TFile, r: float32) = fprintf(f, "%g", r)
|
||||||
proc write(f: TFile, r: biggestFloat) = fprintf(f, "%g", r)
|
proc write(f: TFile, r: biggestFloat) = fprintf(f, "%g", r)
|
||||||
|
|
||||||
proc write(f: TFile, c: Char) = putc(c, f)
|
proc write(f: TFile, c: char) = putc(c, f)
|
||||||
proc write(f: TFile, a: varargs[string, `$`]) =
|
proc write(f: TFile, a: varargs[string, `$`]) =
|
||||||
for x in items(a): write(f, x)
|
for x in items(a): write(f, x)
|
||||||
|
|
||||||
|
|
@ -184,7 +184,7 @@ when defined(windows) and not defined(useWinAnsi):
|
||||||
proc wfreopen(filename, mode: widecstring, stream: TFile): TFile {.
|
proc wfreopen(filename, mode: widecstring, stream: TFile): TFile {.
|
||||||
importc: "_wfreopen", nodecl.}
|
importc: "_wfreopen", nodecl.}
|
||||||
|
|
||||||
proc fopen(filename, mode: CString): pointer =
|
proc fopen(filename, mode: cstring): pointer =
|
||||||
var f = newWideCString(filename)
|
var f = newWideCString(filename)
|
||||||
var m = newWideCString(mode)
|
var m = newWideCString(mode)
|
||||||
result = wfopen(f, m)
|
result = wfopen(f, m)
|
||||||
|
|
@ -195,7 +195,7 @@ when defined(windows) and not defined(useWinAnsi):
|
||||||
result = wfreopen(f, m, stream)
|
result = wfreopen(f, m, stream)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
proc fopen(filename, mode: CString): pointer {.importc: "fopen", noDecl.}
|
proc fopen(filename, mode: cstring): pointer {.importc: "fopen", noDecl.}
|
||||||
proc freopen(filename, mode: cstring, stream: TFile): TFile {.
|
proc freopen(filename, mode: cstring, stream: TFile): TFile {.
|
||||||
importc: "freopen", nodecl.}
|
importc: "freopen", nodecl.}
|
||||||
|
|
||||||
|
|
@ -229,7 +229,7 @@ proc open(f: var TFile, filehandle: TFileHandle, mode: TFileMode): bool =
|
||||||
f = fdopen(filehandle, FormatOpen[mode])
|
f = fdopen(filehandle, FormatOpen[mode])
|
||||||
result = f != nil
|
result = f != nil
|
||||||
|
|
||||||
proc fwrite(buf: Pointer, size, n: int, f: TFile): int {.
|
proc fwrite(buf: pointer, size, n: int, f: TFile): int {.
|
||||||
importc: "fwrite", noDecl.}
|
importc: "fwrite", noDecl.}
|
||||||
|
|
||||||
proc readBuffer(f: TFile, buffer: pointer, len: int): int =
|
proc readBuffer(f: TFile, buffer: pointer, len: int): int =
|
||||||
|
|
|
||||||
9
tests/templates/mcan_access_hidden_field.nim
Normal file
9
tests/templates/mcan_access_hidden_field.nim
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
type
|
||||||
|
Foo* = object
|
||||||
|
fooa, foob: int
|
||||||
|
|
||||||
|
proc createFoo*(a, b: int): Foo = Foo(fooa: a, foob: b)
|
||||||
|
|
||||||
|
template geta*(f: Foo): expr = f.fooa
|
||||||
|
|
||||||
9
tests/templates/tcan_access_hidden_field.nim
Normal file
9
tests/templates/tcan_access_hidden_field.nim
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
discard """
|
||||||
|
output: 33
|
||||||
|
"""
|
||||||
|
|
||||||
|
import mcan_access_hidden_field
|
||||||
|
|
||||||
|
var myfoo = createFoo(33, 44)
|
||||||
|
|
||||||
|
echo myfoo.geta
|
||||||
|
|
@ -30,6 +30,10 @@ Changes affecting backwards compatibility
|
||||||
- ``os.parentDir`` now returns "" if there is no parent dir.
|
- ``os.parentDir`` now returns "" if there is no parent dir.
|
||||||
- In CGI scripts stacktraces are shown user only if cgi.setStackTraceStdout
|
- In CGI scripts stacktraces are shown user only if cgi.setStackTraceStdout
|
||||||
is used.
|
is used.
|
||||||
|
- The symbol binding rules for clean templates changed: ``bind`` for any
|
||||||
|
symbol that's not a parameter is now the default. ``mixin`` can be used
|
||||||
|
to require instantiation scope for a symbol.
|
||||||
|
|
||||||
|
|
||||||
Compiler Additions
|
Compiler Additions
|
||||||
------------------
|
------------------
|
||||||
|
|
@ -60,6 +64,7 @@ Language Additions
|
||||||
- Added ``delegator pragma`` for handling calls to missing procs and fields at
|
- Added ``delegator pragma`` for handling calls to missing procs and fields at
|
||||||
compile-time.
|
compile-time.
|
||||||
- Support for user-defined type classes has been added.
|
- Support for user-defined type classes has been added.
|
||||||
|
- Exported templates are allowed to access hidden fields.
|
||||||
|
|
||||||
|
|
||||||
Tools improvements
|
Tools improvements
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue