fixes #2590; methods now require a .base annotation
This commit is contained in:
parent
d2e4d6ad82
commit
dc047931bb
17 changed files with 69 additions and 32 deletions
|
|
@ -297,6 +297,7 @@ const
|
||||||
sfGoto* = sfOverriden # var is used for 'goto' code generation
|
sfGoto* = sfOverriden # var is used for 'goto' code generation
|
||||||
sfWrittenTo* = sfBorrow # param is assigned to
|
sfWrittenTo* = sfBorrow # param is assigned to
|
||||||
sfEscapes* = sfProcvar # param escapes
|
sfEscapes* = sfProcvar # param escapes
|
||||||
|
sfBase* = sfDiscriminant
|
||||||
|
|
||||||
const
|
const
|
||||||
# getting ready for the future expr/stmt merge
|
# getting ready for the future expr/stmt merge
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,10 @@ proc methodCall*(n: PNode): PNode =
|
||||||
var
|
var
|
||||||
gMethods: seq[tuple[methods: TSymSeq, dispatcher: PSym]] = @[]
|
gMethods: seq[tuple[methods: TSymSeq, dispatcher: PSym]] = @[]
|
||||||
|
|
||||||
proc sameMethodBucket(a, b: PSym): bool =
|
type
|
||||||
result = false
|
MethodResult = enum No, Invalid, Yes
|
||||||
|
|
||||||
|
proc sameMethodBucket(a, b: PSym): MethodResult =
|
||||||
if a.name.id != b.name.id: return
|
if a.name.id != b.name.id: return
|
||||||
if sonsLen(a.typ) != sonsLen(b.typ):
|
if sonsLen(a.typ) != sonsLen(b.typ):
|
||||||
return # check for return type:
|
return # check for return type:
|
||||||
|
|
@ -64,13 +66,15 @@ proc sameMethodBucket(a, b: PSym): bool =
|
||||||
bb = bb.lastSon
|
bb = bb.lastSon
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
if sameType(aa, bb) or
|
if sameType(aa, bb): discard
|
||||||
(aa.kind == tyObject) and (bb.kind == tyObject) and
|
elif aa.kind == tyObject and bb.kind == tyObject:
|
||||||
(inheritanceDiff(bb, aa) < 0):
|
let diff = inheritanceDiff(bb, aa)
|
||||||
discard
|
if diff < 0: discard "Ok"
|
||||||
|
elif diff != high(int):
|
||||||
|
result = Invalid
|
||||||
else:
|
else:
|
||||||
return
|
return No
|
||||||
result = true
|
if result != Invalid: result = Yes
|
||||||
|
|
||||||
proc attachDispatcher(s: PSym, dispatcher: PNode) =
|
proc attachDispatcher(s: PSym, dispatcher: PNode) =
|
||||||
var L = s.ast.len-1
|
var L = s.ast.len-1
|
||||||
|
|
@ -133,18 +137,31 @@ proc fixupDispatcher(meth, disp: PSym) =
|
||||||
|
|
||||||
proc methodDef*(s: PSym, fromCache: bool) =
|
proc methodDef*(s: PSym, fromCache: bool) =
|
||||||
var L = len(gMethods)
|
var L = len(gMethods)
|
||||||
|
var witness: PSym
|
||||||
for i in countup(0, L - 1):
|
for i in countup(0, L - 1):
|
||||||
var disp = gMethods[i].dispatcher
|
var disp = gMethods[i].dispatcher
|
||||||
if sameMethodBucket(disp, s):
|
case sameMethodBucket(disp, s)
|
||||||
|
of Yes:
|
||||||
add(gMethods[i].methods, s)
|
add(gMethods[i].methods, s)
|
||||||
attachDispatcher(s, lastSon(disp.ast))
|
attachDispatcher(s, lastSon(disp.ast))
|
||||||
fixupDispatcher(s, disp)
|
fixupDispatcher(s, disp)
|
||||||
when useEffectSystem: checkMethodEffects(disp, s)
|
when useEffectSystem: checkMethodEffects(disp, s)
|
||||||
|
if sfBase in s.flags and gMethods[i].methods[0] != s:
|
||||||
|
# already exists due to forwarding definition?
|
||||||
|
localError(s.info, "method is not a base")
|
||||||
return
|
return
|
||||||
|
of No: discard
|
||||||
|
of Invalid:
|
||||||
|
if witness.isNil: witness = gMethods[i].methods[0]
|
||||||
# create a new dispatcher:
|
# create a new dispatcher:
|
||||||
add(gMethods, (methods: @[s], dispatcher: createDispatcher(s)))
|
add(gMethods, (methods: @[s], dispatcher: createDispatcher(s)))
|
||||||
if fromCache:
|
if fromCache:
|
||||||
internalError(s.info, "no method dispatcher found")
|
internalError(s.info, "no method dispatcher found")
|
||||||
|
if witness != nil:
|
||||||
|
localError(s.info, "invalid declaration order; cannot attach '" & s.name.s &
|
||||||
|
"' to method defined here: " & $witness.info)
|
||||||
|
elif sfBase notin s.flags:
|
||||||
|
message(s.info, warnUseBase)
|
||||||
|
|
||||||
proc relevantCol(methods: TSymSeq, col: int): bool =
|
proc relevantCol(methods: TSymSeq, col: int): bool =
|
||||||
# returns true iff the position is relevant
|
# returns true iff the position is relevant
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ type
|
||||||
warnUnknownSubstitutionX, warnLanguageXNotSupported,
|
warnUnknownSubstitutionX, warnLanguageXNotSupported,
|
||||||
warnFieldXNotSupported, warnCommentXIgnored,
|
warnFieldXNotSupported, warnCommentXIgnored,
|
||||||
warnNilStatement, warnTypelessParam,
|
warnNilStatement, warnTypelessParam,
|
||||||
warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode,
|
warnUseBase, warnWriteToForeignHeap, warnUnsafeCode,
|
||||||
warnEachIdentIsTuple, warnShadowIdent,
|
warnEachIdentIsTuple, warnShadowIdent,
|
||||||
warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
|
warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
|
||||||
warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
|
warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
|
||||||
|
|
@ -391,7 +391,7 @@ const
|
||||||
warnCommentXIgnored: "comment \'$1\' ignored",
|
warnCommentXIgnored: "comment \'$1\' ignored",
|
||||||
warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead",
|
warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead",
|
||||||
warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'",
|
warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'",
|
||||||
warnDifferentHeaps: "possible inconsistency of thread local heaps",
|
warnUseBase: "use {.base.} for base methods; baseless methods are deprecated",
|
||||||
warnWriteToForeignHeap: "write to foreign heap",
|
warnWriteToForeignHeap: "write to foreign heap",
|
||||||
warnUnsafeCode: "unsafe code: '$1'",
|
warnUnsafeCode: "unsafe code: '$1'",
|
||||||
warnEachIdentIsTuple: "each identifier is a tuple",
|
warnEachIdentIsTuple: "each identifier is a tuple",
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ const
|
||||||
wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe,
|
wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe,
|
||||||
wOverride, wConstructor}
|
wOverride, wConstructor}
|
||||||
converterPragmas* = procPragmas
|
converterPragmas* = procPragmas
|
||||||
methodPragmas* = procPragmas
|
methodPragmas* = procPragmas+{wBase}
|
||||||
templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty,
|
templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty,
|
||||||
wDelegator}
|
wDelegator}
|
||||||
macroPragmas* = {FirstCallConv..LastCallConv, wImmediate, wImportc, wExportc,
|
macroPragmas* = {FirstCallConv..LastCallConv, wImmediate, wImportc, wExportc,
|
||||||
|
|
@ -867,6 +867,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||||
localError(it.info, "'experimental' pragma only valid as toplevel statement")
|
localError(it.info, "'experimental' pragma only valid as toplevel statement")
|
||||||
of wNoRewrite:
|
of wNoRewrite:
|
||||||
noVal(it)
|
noVal(it)
|
||||||
|
of wBase:
|
||||||
|
noVal(it)
|
||||||
|
sym.flags.incl sfBase
|
||||||
else: invalidPragma(it)
|
else: invalidPragma(it)
|
||||||
else: invalidPragma(it)
|
else: invalidPragma(it)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ type
|
||||||
wImportc, wExportc, wIncompleteStruct, wRequiresInit,
|
wImportc, wExportc, wIncompleteStruct, wRequiresInit,
|
||||||
wAlign, wNodecl, wPure, wSideeffect, wHeader,
|
wAlign, wNodecl, wPure, wSideeffect, wHeader,
|
||||||
wNosideeffect, wGcSafe, wNoreturn, wMerge, wLib, wDynlib,
|
wNosideeffect, wGcSafe, wNoreturn, wMerge, wLib, wDynlib,
|
||||||
wCompilerproc, wProcVar,
|
wCompilerproc, wProcVar, wBase,
|
||||||
wFatal, wError, wWarning, wHint, wLine, wPush, wPop, wDefine, wUndef,
|
wFatal, wError, wWarning, wHint, wLine, wPush, wPop, wDefine, wUndef,
|
||||||
wLinedir, wStacktrace, wLinetrace, wLink, wCompile,
|
wLinedir, wStacktrace, wLinetrace, wLink, wCompile,
|
||||||
wLinksys, wDeprecated, wVarargs, wCallconv, wBreakpoint, wDebugger,
|
wLinksys, wDeprecated, wVarargs, wCallconv, wBreakpoint, wDebugger,
|
||||||
|
|
@ -128,7 +128,8 @@ const
|
||||||
"importcompilerproc", "importc", "exportc", "incompletestruct",
|
"importcompilerproc", "importc", "exportc", "incompletestruct",
|
||||||
"requiresinit", "align", "nodecl", "pure", "sideeffect",
|
"requiresinit", "align", "nodecl", "pure", "sideeffect",
|
||||||
"header", "nosideeffect", "gcsafe", "noreturn", "merge", "lib", "dynlib",
|
"header", "nosideeffect", "gcsafe", "noreturn", "merge", "lib", "dynlib",
|
||||||
"compilerproc", "procvar", "fatal", "error", "warning", "hint", "line",
|
"compilerproc", "procvar", "base",
|
||||||
|
"fatal", "error", "warning", "hint", "line",
|
||||||
"push", "pop", "define", "undef", "linedir", "stacktrace", "linetrace",
|
"push", "pop", "define", "undef", "linedir", "stacktrace", "linetrace",
|
||||||
"link", "compile", "linksys", "deprecated", "varargs",
|
"link", "compile", "linksys", "deprecated", "varargs",
|
||||||
"callconv", "breakpoint", "debugger", "nimcall", "stdcall",
|
"callconv", "breakpoint", "debugger", "nimcall", "stdcall",
|
||||||
|
|
|
||||||
|
|
@ -385,7 +385,7 @@ dispatch.
|
||||||
PlusExpr = ref object of Expression
|
PlusExpr = ref object of Expression
|
||||||
a, b: Expression
|
a, b: Expression
|
||||||
|
|
||||||
method eval(e: Expression): int =
|
method eval(e: Expression): int {.base.} =
|
||||||
# override this base method
|
# override this base method
|
||||||
quit "to override!"
|
quit "to override!"
|
||||||
|
|
||||||
|
|
@ -410,6 +410,11 @@ In the example the constructors ``newLit`` and ``newPlus`` are procs
|
||||||
because they should use static binding, but ``eval`` is a method because it
|
because they should use static binding, but ``eval`` is a method because it
|
||||||
requires dynamic binding.
|
requires dynamic binding.
|
||||||
|
|
||||||
|
As can be seen in the example, base methods have to be annotated with
|
||||||
|
the `base`:idx: pragma. The ``base`` pragma also acts as a reminder for the
|
||||||
|
programmer that a base method ``m`` is used as the foundation to determine all
|
||||||
|
the effects that a call to ``m`` might cause.
|
||||||
|
|
||||||
In a multi-method all parameters that have an object type are used for the
|
In a multi-method all parameters that have an object type are used for the
|
||||||
dispatching:
|
dispatching:
|
||||||
|
|
||||||
|
|
@ -419,7 +424,7 @@ dispatching:
|
||||||
Unit = ref object of Thing
|
Unit = ref object of Thing
|
||||||
x: int
|
x: int
|
||||||
|
|
||||||
method collide(a, b: Thing) {.inline.} =
|
method collide(a, b: Thing) {.base, inline.} =
|
||||||
quit "to override!"
|
quit "to override!"
|
||||||
|
|
||||||
method collide(a: Thing, b: Unit) {.inline.} =
|
method collide(a: Thing, b: Unit) {.inline.} =
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ type
|
||||||
|
|
||||||
var myObj* : ref TObj
|
var myObj* : ref TObj
|
||||||
|
|
||||||
method test123(a : ref TObj) =
|
method test123(a : ref TObj) {.base.} =
|
||||||
echo("Hi base!")
|
echo("Hi base!")
|
||||||
|
|
||||||
proc testMyObj*() =
|
proc testMyObj*() =
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
type MyClass = ref object of RootObj
|
type MyClass = ref object of RootObj
|
||||||
|
|
||||||
method HelloWorld*(obj: MyClass) =
|
method HelloWorld*(obj: MyClass) {.base.} =
|
||||||
when defined(myPragma):
|
when defined(myPragma):
|
||||||
echo("Hello World")
|
echo("Hello World")
|
||||||
# discard # with this line enabled it works
|
# discard # with this line enabled it works
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ discard """
|
||||||
output: "do nothing"
|
output: "do nothing"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
method somethin(obj: TObject) =
|
method somethin(obj: RootObj) {.base.} =
|
||||||
echo "do nothing"
|
echo "do nothing"
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -14,9 +14,9 @@ type
|
||||||
TSomethingElse = object
|
TSomethingElse = object
|
||||||
PSomethingElse = ref TSomethingElse
|
PSomethingElse = ref TSomethingElse
|
||||||
|
|
||||||
method foo(a: PNode, b: PSomethingElse) = discard
|
method foo(a: PNode, b: PSomethingElse) {.base.} = discard
|
||||||
method foo(a: PNodeFoo, b: PSomethingElse) = discard
|
method foo(a: PNodeFoo, b: PSomethingElse) = discard
|
||||||
|
|
||||||
var o: TObject
|
var o: RootObj
|
||||||
o.somethin()
|
o.somethin()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ type
|
||||||
Obj1 = ref object {.inheritable.}
|
Obj1 = ref object {.inheritable.}
|
||||||
Obj2 = ref object of Obj1
|
Obj2 = ref object of Obj1
|
||||||
|
|
||||||
method beta(x: Obj1): int
|
method beta(x: Obj1): int {.base.}
|
||||||
|
|
||||||
proc delta(x: Obj2): int =
|
proc delta(x: Obj2): int =
|
||||||
beta(x)
|
beta(x)
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ type
|
||||||
PlusExpr = ref object of Expression
|
PlusExpr = ref object of Expression
|
||||||
a, b: Expression
|
a, b: Expression
|
||||||
|
|
||||||
method eval(e: Expression): int = quit "to override!"
|
method eval(e: Expression): int {.base.} = quit "to override!"
|
||||||
method eval(e: Literal): int = return e.x
|
method eval(e: Literal): int = return e.x
|
||||||
method eval(e: PlusExpr): int = return eval(e.a) + eval(e.b)
|
method eval(e: PlusExpr): int = return eval(e.a) + eval(e.b)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ type
|
||||||
TParticle = object of TThing
|
TParticle = object of TThing
|
||||||
a, b: int
|
a, b: int
|
||||||
|
|
||||||
method collide(a, b: TThing) {.inline.} =
|
method collide(a, b: TThing) {.base, inline.} =
|
||||||
echo "collide: thing, thing"
|
echo "collide: thing, thing"
|
||||||
|
|
||||||
method collide(a: TThing, b: TUnit) {.inline.} =
|
method collide(a: TThing, b: TUnit) {.inline.} =
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ discard """
|
||||||
type
|
type
|
||||||
Test = object of TObject
|
Test = object of TObject
|
||||||
|
|
||||||
method doMethod(a: ref TObject) {.raises: [EIO].} =
|
method doMethod(a: ref TObject) {.base, raises: [EIO].} =
|
||||||
quit "override"
|
quit "override"
|
||||||
|
|
||||||
method doMethod(a: ref Test) =
|
method doMethod(a: ref Test) =
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ type
|
||||||
TParticle = object of TThing
|
TParticle = object of TThing
|
||||||
a, b: int
|
a, b: int
|
||||||
|
|
||||||
method collide(a, b: TThing) {.inline.} =
|
method collide(a, b: TThing) {.base, inline.} =
|
||||||
quit "to override!"
|
quit "to override!"
|
||||||
|
|
||||||
method collide[T](a: TThing, b: TUnit[T]) {.inline.} =
|
method collide[T](a: TThing, b: TUnit[T]) {.inline.} =
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,12 @@
|
||||||
# for recursive methods works, no code is being executed
|
# for recursive methods works, no code is being executed
|
||||||
|
|
||||||
type
|
type
|
||||||
Obj = ref object of TObject
|
Obj = ref object of RootObj
|
||||||
|
|
||||||
# Mutual recursion
|
# Mutual recursion
|
||||||
|
|
||||||
method alpha(x: Obj)
|
method alpha(x: Obj) {.base.}
|
||||||
method beta(x: Obj)
|
method beta(x: Obj) {.base.}
|
||||||
|
|
||||||
method alpha(x: Obj) =
|
method alpha(x: Obj) =
|
||||||
beta(x)
|
beta(x)
|
||||||
|
|
@ -17,6 +17,6 @@ method beta(x: Obj) =
|
||||||
|
|
||||||
# Simple recursion
|
# Simple recursion
|
||||||
|
|
||||||
method gamma(x: Obj) =
|
method gamma(x: Obj) {.base.} =
|
||||||
gamma(x)
|
gamma(x)
|
||||||
|
|
||||||
|
|
|
||||||
1
todo.txt
1
todo.txt
|
|
@ -13,7 +13,6 @@ version 0.11.4
|
||||||
|
|
||||||
- add "all threads are blocked" detection to 'spawn'
|
- add "all threads are blocked" detection to 'spawn'
|
||||||
- Deprecate ``immediate`` for templates and macros
|
- Deprecate ``immediate`` for templates and macros
|
||||||
- make people annotate .anchor methods
|
|
||||||
|
|
||||||
|
|
||||||
version 1.0
|
version 1.0
|
||||||
|
|
|
||||||
13
web/news.txt
13
web/news.txt
|
|
@ -50,6 +50,9 @@ News
|
||||||
and are now deprecated and will be removed from the language. Instead you
|
and are now deprecated and will be removed from the language. Instead you
|
||||||
have to insert type conversions
|
have to insert type conversions
|
||||||
like ``(proc (a, b: int) {.closure.})(myToplevelProc)`` if necessary.
|
like ``(proc (a, b: int) {.closure.})(myToplevelProc)`` if necessary.
|
||||||
|
- The modules ``libffi``, ``sdl``, ``windows``, ``zipfiles``, ``libzip``,
|
||||||
|
``zlib``, ``zzip``, ``dialogs``, ``expat``, ``graphics``, ``libcurl``,
|
||||||
|
``sphinx`` have been moved out of the stdlib and are Nimble packages now.
|
||||||
- The constant fights between 32 and 64 bit DLLs on Windows have been put to
|
- The constant fights between 32 and 64 bit DLLs on Windows have been put to
|
||||||
an end: The standard distribution now ships with 32 and 64 bit versions
|
an end: The standard distribution now ships with 32 and 64 bit versions
|
||||||
of all the DLLs the standard library needs. This means that the following
|
of all the DLLs the standard library needs. This means that the following
|
||||||
|
|
@ -66,9 +69,11 @@ News
|
||||||
was previously. Macros that generate nkPar nodes when object is expected are
|
was previously. Macros that generate nkPar nodes when object is expected are
|
||||||
likely to break. Macros that expect nkPar nodes to which objects are passed
|
likely to break. Macros that expect nkPar nodes to which objects are passed
|
||||||
are likely to break as well.
|
are likely to break as well.
|
||||||
|
- Base methods now need to be annotated with the ``base`` pragma. This makes
|
||||||
|
multi methods less error-prone to use with the effect system.
|
||||||
|
|
||||||
|
|
||||||
Library additions
|
Library Additions
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
- The nre module has been added, providing a better interface to PCRE than re.
|
- The nre module has been added, providing a better interface to PCRE than re.
|
||||||
|
|
@ -81,6 +86,12 @@ News
|
||||||
locale anymore and now take an optional ``decimalSep = '.'`` parameter.
|
locale anymore and now take an optional ``decimalSep = '.'`` parameter.
|
||||||
|
|
||||||
|
|
||||||
|
Compiler Additions
|
||||||
|
------------------
|
||||||
|
|
||||||
|
- The compiler now supports a new configuration system based on ``NimScript``.
|
||||||
|
|
||||||
|
|
||||||
Language Additions
|
Language Additions
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue