fixes #2590; methods now require a .base annotation

This commit is contained in:
Araq 2015-09-06 02:29:30 +02:00
commit dc047931bb
17 changed files with 69 additions and 32 deletions

View file

@ -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

View file

@ -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

View file

@ -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",

View file

@ -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)

View file

@ -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",

View file

@ -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.} =

View file

@ -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*() =

View file

@ -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

View file

@ -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()

View file

@ -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)

View file

@ -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)

View file

@ -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.} =

View file

@ -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) =

View file

@ -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.} =

View file

@ -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)

View file

@ -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

View file

@ -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
------------------ ------------------