implemented optional pragma for implicit discard
This commit is contained in:
parent
033e3dfc50
commit
72ceda98cb
11 changed files with 53 additions and 21 deletions
|
|
@ -197,7 +197,7 @@ type
|
||||||
# be written into the ROD file
|
# be written into the ROD file
|
||||||
sfGlobal, # symbol is at global scope
|
sfGlobal, # symbol is at global scope
|
||||||
|
|
||||||
sfForward, # symbol is forward directed
|
sfForward, # symbol is forward declared
|
||||||
sfImportc, # symbol is external; imported
|
sfImportc, # symbol is external; imported
|
||||||
sfExportc, # symbol is exported (under a specified name)
|
sfExportc, # symbol is exported (under a specified name)
|
||||||
sfVolatile, # variable is volatile
|
sfVolatile, # variable is volatile
|
||||||
|
|
@ -218,15 +218,16 @@ type
|
||||||
sfError, # usage of symbol should trigger a compile-time error
|
sfError, # usage of symbol should trigger a compile-time error
|
||||||
sfInClosure, # variable is accessed by a closure
|
sfInClosure, # variable is accessed by a closure
|
||||||
sfThread, # proc will run as a thread
|
sfThread, # proc will run as a thread
|
||||||
|
# variable is a thread variable
|
||||||
sfCompileTime, # proc can be evaluated at compile time
|
sfCompileTime, # proc can be evaluated at compile time
|
||||||
sfThreadVar, # variable is a thread variable
|
|
||||||
sfMerge, # proc can be merged with itself
|
sfMerge, # proc can be merged with itself
|
||||||
sfDeadCodeElim, # dead code elimination for the module is turned on
|
sfDeadCodeElim, # dead code elimination for the module is turned on
|
||||||
sfBorrow, # proc is borrowed
|
sfBorrow, # proc is borrowed
|
||||||
sfInfixCall, # symbol needs infix call syntax in target language;
|
sfInfixCall, # symbol needs infix call syntax in target language;
|
||||||
# for interfacing with C++, JS
|
# for interfacing with C++, JS
|
||||||
sfNamedParamCall # symbol needs named parameter call syntax in target
|
sfNamedParamCall, # symbol needs named parameter call syntax in target
|
||||||
# language; for interfacing with Objective C
|
# language; for interfacing with Objective C
|
||||||
|
sfOptional # returned value may be discarded implicitely
|
||||||
|
|
||||||
TSymFlags* = set[TSymFlag]
|
TSymFlags* = set[TSymFlag]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1761,7 +1761,7 @@ proc expr(p: BProc, e: PNode, d: var TLoc) =
|
||||||
if sfGlobal in sym.flags: genVarPrototype(p.module, sym)
|
if sfGlobal in sym.flags: genVarPrototype(p.module, sym)
|
||||||
if sym.loc.r == nil or sym.loc.t == nil:
|
if sym.loc.r == nil or sym.loc.t == nil:
|
||||||
InternalError(e.info, "expr: var not init " & sym.name.s)
|
InternalError(e.info, "expr: var not init " & sym.name.s)
|
||||||
if sfThreadVar in sym.flags:
|
if sfThread in sym.flags:
|
||||||
AccessThreadLocalVar(p, sym)
|
AccessThreadLocalVar(p, sym)
|
||||||
if emulatedThreadVars():
|
if emulatedThreadVars():
|
||||||
putIntoDest(p, d, sym.loc.t, con("NimTV->", sym.loc.r))
|
putIntoDest(p, d, sym.loc.t, con("NimTV->", sym.loc.r))
|
||||||
|
|
|
||||||
|
|
@ -445,7 +445,7 @@ proc assignGlobalVar(p: BProc, s: PSym) =
|
||||||
fillLoc(s.loc, locGlobalVar, s.typ, mangleName(s), OnHeap)
|
fillLoc(s.loc, locGlobalVar, s.typ, mangleName(s), OnHeap)
|
||||||
useHeader(p.module, s)
|
useHeader(p.module, s)
|
||||||
if lfNoDecl in s.loc.flags: return
|
if lfNoDecl in s.loc.flags: return
|
||||||
if sfThreadVar in s.flags:
|
if sfThread in s.flags:
|
||||||
declareThreadVar(p.module, s, sfImportc in s.flags)
|
declareThreadVar(p.module, s, sfImportc in s.flags)
|
||||||
else:
|
else:
|
||||||
if sfImportc in s.flags: app(p.module.s[cfsVars], "extern ")
|
if sfImportc in s.flags: app(p.module.s[cfsVars], "extern ")
|
||||||
|
|
@ -724,7 +724,7 @@ proc genVarPrototype(m: BModule, sym: PSym) =
|
||||||
if sym.owner.id != m.module.id:
|
if sym.owner.id != m.module.id:
|
||||||
# else we already have the symbol generated!
|
# else we already have the symbol generated!
|
||||||
assert(sym.loc.r != nil)
|
assert(sym.loc.r != nil)
|
||||||
if sfThreadVar in sym.flags:
|
if sfThread in sym.flags:
|
||||||
declareThreadVar(m, sym, true)
|
declareThreadVar(m, sym, true)
|
||||||
else:
|
else:
|
||||||
app(m.s[cfsVars], "extern ")
|
app(m.s[cfsVars], "extern ")
|
||||||
|
|
|
||||||
|
|
@ -23,15 +23,15 @@ const
|
||||||
wMagic, wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader,
|
wMagic, wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader,
|
||||||
wCompilerProc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
|
wCompilerProc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
|
||||||
wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
|
wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
|
||||||
wNoStackFrame, wError}
|
wNoStackFrame, wError, wOptional}
|
||||||
converterPragmas* = procPragmas
|
converterPragmas* = procPragmas
|
||||||
methodPragmas* = procPragmas
|
methodPragmas* = procPragmas
|
||||||
macroPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
|
macroPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
|
||||||
wMagic, wNosideEffect, wCompilerProc, wDeprecated, wExtern,
|
wMagic, wNosideEffect, wCompilerProc, wDeprecated, wExtern,
|
||||||
wImportcpp, wImportobjc, wError}
|
wImportcpp, wImportobjc, wError, wOptional}
|
||||||
iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideEffect, wSideEffect,
|
iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideEffect, wSideEffect,
|
||||||
wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
|
wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
|
||||||
wImportcpp, wImportobjc, wError}
|
wImportcpp, wImportobjc, wError, wOptional}
|
||||||
stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
|
stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
|
||||||
wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, wLinedir,
|
wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, wLinedir,
|
||||||
wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError, wFatal,
|
wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError, wFatal,
|
||||||
|
|
@ -461,7 +461,7 @@ proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
|
||||||
incl(sym.flags, sfRegister)
|
incl(sym.flags, sfRegister)
|
||||||
of wThreadVar:
|
of wThreadVar:
|
||||||
noVal(it)
|
noVal(it)
|
||||||
incl(sym.flags, sfThreadVar)
|
incl(sym.flags, sfThread)
|
||||||
of wDeadCodeElim: pragmaDeadCodeElim(c, it)
|
of wDeadCodeElim: pragmaDeadCodeElim(c, it)
|
||||||
of wMagic: processMagic(c, it, sym)
|
of wMagic: processMagic(c, it, sym)
|
||||||
of wCompileTime:
|
of wCompileTime:
|
||||||
|
|
@ -553,6 +553,9 @@ proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
|
||||||
of wPragma:
|
of wPragma:
|
||||||
processPragma(c, n, i)
|
processPragma(c, n, i)
|
||||||
break
|
break
|
||||||
|
of wOptional:
|
||||||
|
noVal(it)
|
||||||
|
if sym != nil: incl(sym.flags, sfOptional)
|
||||||
of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
|
of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
|
||||||
wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
|
wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
|
||||||
wLinedir, wStacktrace, wLinetrace, wOptimization, wByRef,
|
wLinedir, wStacktrace, wLinetrace, wOptimization, wByRef,
|
||||||
|
|
|
||||||
|
|
@ -548,11 +548,14 @@ proc buildEchoStmt(c: PContext, n: PNode): PNode =
|
||||||
addSon(result, semExpr(c, arg))
|
addSon(result, semExpr(c, arg))
|
||||||
|
|
||||||
proc semExprNoType(c: PContext, n: PNode): PNode =
|
proc semExprNoType(c: PContext, n: PNode): PNode =
|
||||||
|
proc ImplicitelyDiscardable(n: PNode): bool {.inline.} =
|
||||||
|
result = isCallExpr(n) and n.sons[0].kind == nkSym and
|
||||||
|
sfOptional in n.sons[0].sym.flags
|
||||||
result = semExpr(c, n)
|
result = semExpr(c, n)
|
||||||
if result.typ != nil and result.typ.kind != tyStmt:
|
if result.typ != nil and result.typ.kind != tyStmt:
|
||||||
if gCmd == cmdInteractive:
|
if gCmd == cmdInteractive:
|
||||||
result = buildEchoStmt(c, result)
|
result = buildEchoStmt(c, result)
|
||||||
else:
|
elif not ImplicitelyDiscardable(result):
|
||||||
localError(n.info, errDiscardValue)
|
localError(n.info, errDiscardValue)
|
||||||
|
|
||||||
proc isTypeExpr(n: PNode): bool =
|
proc isTypeExpr(n: PNode): bool =
|
||||||
|
|
@ -1196,7 +1199,6 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
of skMacro: result = semMacroExpr(c, n, s)
|
of skMacro: result = semMacroExpr(c, n, s)
|
||||||
of skTemplate: result = semTemplateExpr(c, n, s)
|
of skTemplate: result = semTemplateExpr(c, n, s)
|
||||||
of skType:
|
of skType:
|
||||||
if n.kind != nkCall: GlobalError(n.info, errXisNotCallable, s.name.s)
|
|
||||||
# XXX think about this more (``set`` procs)
|
# XXX think about this more (``set`` procs)
|
||||||
if n.len == 2:
|
if n.len == 2:
|
||||||
result = semConv(c, n, s)
|
result = semConv(c, n, s)
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ proc analyseSym(c: PProcCtx, n: PNode): TThreadOwner =
|
||||||
of skVar, skResult:
|
of skVar, skResult:
|
||||||
result = toNil
|
result = toNil
|
||||||
if sfGlobal in v.flags:
|
if sfGlobal in v.flags:
|
||||||
if sfThreadVar in v.flags:
|
if sfThread in v.flags:
|
||||||
result = toMine
|
result = toMine
|
||||||
elif containsGarbageCollectedRef(v.typ):
|
elif containsGarbageCollectedRef(v.typ):
|
||||||
result = toTheirs
|
result = toTheirs
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ type
|
||||||
wDeadCodeElim, wSafecode,
|
wDeadCodeElim, wSafecode,
|
||||||
wPragma,
|
wPragma,
|
||||||
wCompileTime,
|
wCompileTime,
|
||||||
wPassc, wPassl, wBorrow,
|
wPassc, wPassl, wBorrow, wOptional,
|
||||||
wFieldChecks,
|
wFieldChecks,
|
||||||
wCheckPoint, wSubsChar,
|
wCheckPoint, wSubsChar,
|
||||||
wAcyclic, wShallow, wUnroll, wLinearScanEnd,
|
wAcyclic, wShallow, wUnroll, wLinearScanEnd,
|
||||||
|
|
@ -96,7 +96,7 @@ const
|
||||||
"deadcodeelim", "safecode",
|
"deadcodeelim", "safecode",
|
||||||
"pragma",
|
"pragma",
|
||||||
"compiletime",
|
"compiletime",
|
||||||
"passc", "passl", "borrow", "fieldchecks",
|
"passc", "passl", "borrow", "optional", "fieldchecks",
|
||||||
"checkpoint",
|
"checkpoint",
|
||||||
"subschar", "acyclic", "shallow", "unroll", "linearscanend",
|
"subschar", "acyclic", "shallow", "unroll", "linearscanend",
|
||||||
"write", "putenv", "prependenv", "appendenv", "threadvar", "emit",
|
"write", "putenv", "prependenv", "appendenv", "threadvar", "emit",
|
||||||
|
|
|
||||||
|
|
@ -1474,14 +1474,26 @@ Syntax::
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nimrod
|
||||||
|
proc p(x, y: int): int {.optional.} =
|
||||||
|
return x + y
|
||||||
|
|
||||||
discard proc_call("arg1", "arg2") # discard the return value of `proc_call`
|
discard p(3, 4) # discard the return value of `p`
|
||||||
|
|
||||||
The `discard`:idx: statement evaluates its expression for side-effects and
|
The `discard`:idx: statement evaluates its expression for side-effects and
|
||||||
throws the expression's resulting value away. If the expression has no
|
throws the expression's resulting value away.
|
||||||
side-effects, this generates a static error. Ignoring the return value of a
|
|
||||||
procedure without using a discard statement is a static error too.
|
Ignoring the return value of a procedure without using a discard statement is
|
||||||
|
a static error.
|
||||||
|
|
||||||
|
The return value can be ignored implicitely if the called proc/iterator has
|
||||||
|
been declared with the `optional`:idx: pragma:
|
||||||
|
|
||||||
|
.. code-block:: nimrod
|
||||||
|
proc p(x, y: int): int {.optional.} =
|
||||||
|
return x + y
|
||||||
|
|
||||||
|
p(3, 4) # now valid
|
||||||
|
|
||||||
|
|
||||||
Var statement
|
Var statement
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
|
||||||
13
tests/accept/compile/toptional_pragma.nim
Normal file
13
tests/accept/compile/toptional_pragma.nim
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
# Test the optional pragma
|
||||||
|
|
||||||
|
proc p(x, y: int): int {.optional.} =
|
||||||
|
return x + y
|
||||||
|
|
||||||
|
# test that it is inherited from generic procs too:
|
||||||
|
proc q[T](x, y: T): T {.optional.} =
|
||||||
|
return x + y
|
||||||
|
|
||||||
|
|
||||||
|
p(8, 2)
|
||||||
|
q[float](0.8, 0.2)
|
||||||
|
|
||||||
1
todo.txt
1
todo.txt
|
|
@ -2,7 +2,6 @@ Version 0.8.14
|
||||||
==============
|
==============
|
||||||
|
|
||||||
- 'let x = y'
|
- 'let x = y'
|
||||||
- T(x) as l-value
|
|
||||||
- fix actors.nim
|
- fix actors.nim
|
||||||
- make threadvar efficient again on linux after testing
|
- make threadvar efficient again on linux after testing
|
||||||
- fix the 'const' issues
|
- fix the 'const' issues
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,8 @@ Language Additions
|
||||||
- Return types may be of the type ``var T`` to return an l-value.
|
- Return types may be of the type ``var T`` to return an l-value.
|
||||||
- The error pragma can now be used to mark symbols whose *usage* should trigger
|
- The error pragma can now be used to mark symbols whose *usage* should trigger
|
||||||
a compile-time error.
|
a compile-time error.
|
||||||
|
- There is a new ``optional`` pragma that can be used to mark a routine so that
|
||||||
|
its result can be discarded implicitely.
|
||||||
|
|
||||||
|
|
||||||
Compiler Additions
|
Compiler Additions
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue