diff --git a/compiler/ast.nim b/compiler/ast.nim index aafe503dc7..177b5c5c72 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -297,6 +297,7 @@ const sfGoto* = sfOverriden # var is used for 'goto' code generation sfWrittenTo* = sfBorrow # param is assigned to sfEscapes* = sfProcvar # param escapes + sfBase* = sfDiscriminant const # getting ready for the future expr/stmt merge diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim index adb4f1f929..d2358b84a6 100644 --- a/compiler/cgmeth.nim +++ b/compiler/cgmeth.nim @@ -47,8 +47,10 @@ proc methodCall*(n: PNode): PNode = var gMethods: seq[tuple[methods: TSymSeq, dispatcher: PSym]] = @[] -proc sameMethodBucket(a, b: PSym): bool = - result = false +type + MethodResult = enum No, Invalid, Yes + +proc sameMethodBucket(a, b: PSym): MethodResult = if a.name.id != b.name.id: return if sonsLen(a.typ) != sonsLen(b.typ): return # check for return type: @@ -64,13 +66,15 @@ proc sameMethodBucket(a, b: PSym): bool = bb = bb.lastSon else: break - if sameType(aa, bb) or - (aa.kind == tyObject) and (bb.kind == tyObject) and - (inheritanceDiff(bb, aa) < 0): - discard + if sameType(aa, bb): discard + elif aa.kind == tyObject and bb.kind == tyObject: + let diff = inheritanceDiff(bb, aa) + if diff < 0: discard "Ok" + elif diff != high(int): + result = Invalid else: - return - result = true + return No + if result != Invalid: result = Yes proc attachDispatcher(s: PSym, dispatcher: PNode) = var L = s.ast.len-1 @@ -133,18 +137,31 @@ proc fixupDispatcher(meth, disp: PSym) = proc methodDef*(s: PSym, fromCache: bool) = var L = len(gMethods) + var witness: PSym for i in countup(0, L - 1): var disp = gMethods[i].dispatcher - if sameMethodBucket(disp, s): + case sameMethodBucket(disp, s) + of Yes: add(gMethods[i].methods, s) attachDispatcher(s, lastSon(disp.ast)) fixupDispatcher(s, disp) 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 + of No: discard + of Invalid: + if witness.isNil: witness = gMethods[i].methods[0] # create a new dispatcher: add(gMethods, (methods: @[s], dispatcher: createDispatcher(s))) if fromCache: 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 = # returns true iff the position is relevant diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 1b1f0a76eb..c815e02777 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -118,7 +118,7 @@ type warnUnknownSubstitutionX, warnLanguageXNotSupported, warnFieldXNotSupported, warnCommentXIgnored, warnNilStatement, warnTypelessParam, - warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode, + warnUseBase, warnWriteToForeignHeap, warnUnsafeCode, warnEachIdentIsTuple, warnShadowIdent, warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed, @@ -391,7 +391,7 @@ const warnCommentXIgnored: "comment \'$1\' ignored", warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead", 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", warnUnsafeCode: "unsafe code: '$1'", warnEachIdentIsTuple: "each identifier is a tuple", diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 5f317ed242..1c51251fe7 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -27,7 +27,7 @@ const wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe, wOverride, wConstructor} converterPragmas* = procPragmas - methodPragmas* = procPragmas + methodPragmas* = procPragmas+{wBase} templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty, wDelegator} 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") of wNoRewrite: noVal(it) + of wBase: + noVal(it) + sym.flags.incl sfBase else: invalidPragma(it) else: invalidPragma(it) diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index deb12536f8..23f012ea50 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -45,7 +45,7 @@ type wImportc, wExportc, wIncompleteStruct, wRequiresInit, wAlign, wNodecl, wPure, wSideeffect, wHeader, wNosideeffect, wGcSafe, wNoreturn, wMerge, wLib, wDynlib, - wCompilerproc, wProcVar, + wCompilerproc, wProcVar, wBase, wFatal, wError, wWarning, wHint, wLine, wPush, wPop, wDefine, wUndef, wLinedir, wStacktrace, wLinetrace, wLink, wCompile, wLinksys, wDeprecated, wVarargs, wCallconv, wBreakpoint, wDebugger, @@ -128,7 +128,8 @@ const "importcompilerproc", "importc", "exportc", "incompletestruct", "requiresinit", "align", "nodecl", "pure", "sideeffect", "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", "link", "compile", "linksys", "deprecated", "varargs", "callconv", "breakpoint", "debugger", "nimcall", "stdcall", diff --git a/doc/manual/procs.txt b/doc/manual/procs.txt index bd86e4651e..9d00b7e3c5 100644 --- a/doc/manual/procs.txt +++ b/doc/manual/procs.txt @@ -385,7 +385,7 @@ dispatch. PlusExpr = ref object of Expression a, b: Expression - method eval(e: Expression): int = + method eval(e: Expression): int {.base.} = # override this base method 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 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 dispatching: @@ -419,7 +424,7 @@ dispatching: Unit = ref object of Thing x: int - method collide(a, b: Thing) {.inline.} = + method collide(a, b: Thing) {.base, inline.} = quit "to override!" method collide(a: Thing, b: Unit) {.inline.} = diff --git a/tests/method/mmultim3.nim b/tests/method/mmultim3.nim index 3139a80899..b391731be7 100644 --- a/tests/method/mmultim3.nim +++ b/tests/method/mmultim3.nim @@ -3,7 +3,7 @@ type var myObj* : ref TObj -method test123(a : ref TObj) = +method test123(a : ref TObj) {.base.} = echo("Hi base!") proc testMyObj*() = diff --git a/tests/method/temptybody.nim b/tests/method/temptybody.nim index 26285d05b7..aad945f81a 100644 --- a/tests/method/temptybody.nim +++ b/tests/method/temptybody.nim @@ -2,7 +2,7 @@ type MyClass = ref object of RootObj -method HelloWorld*(obj: MyClass) = +method HelloWorld*(obj: MyClass) {.base.} = when defined(myPragma): echo("Hello World") # discard # with this line enabled it works diff --git a/tests/method/tmethods1.nim b/tests/method/tmethods1.nim index 461e2cb5ec..cb4da5ef2b 100644 --- a/tests/method/tmethods1.nim +++ b/tests/method/tmethods1.nim @@ -2,7 +2,7 @@ discard """ output: "do nothing" """ -method somethin(obj: TObject) = +method somethin(obj: RootObj) {.base.} = echo "do nothing" type @@ -14,9 +14,9 @@ type TSomethingElse = object 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 -var o: TObject +var o: RootObj o.somethin() diff --git a/tests/method/tmproto.nim b/tests/method/tmproto.nim index 5d75cff1a0..087666ea00 100644 --- a/tests/method/tmproto.nim +++ b/tests/method/tmproto.nim @@ -2,7 +2,7 @@ type Obj1 = ref object {.inheritable.} Obj2 = ref object of Obj1 -method beta(x: Obj1): int +method beta(x: Obj1): int {.base.} proc delta(x: Obj2): int = beta(x) diff --git a/tests/method/tmultim1.nim b/tests/method/tmultim1.nim index c7027f4c0e..010468a5bf 100644 --- a/tests/method/tmultim1.nim +++ b/tests/method/tmultim1.nim @@ -11,7 +11,7 @@ type PlusExpr = ref object of 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: PlusExpr): int = return eval(e.a) + eval(e.b) diff --git a/tests/method/tmultim2.nim b/tests/method/tmultim2.nim index e695dd19be..98a08b1cbd 100644 --- a/tests/method/tmultim2.nim +++ b/tests/method/tmultim2.nim @@ -14,7 +14,7 @@ type TParticle = object of TThing a, b: int -method collide(a, b: TThing) {.inline.} = +method collide(a, b: TThing) {.base, inline.} = echo "collide: thing, thing" method collide(a: TThing, b: TUnit) {.inline.} = diff --git a/tests/method/tmultim4.nim b/tests/method/tmultim4.nim index 54630fb41e..eabf8d126d 100644 --- a/tests/method/tmultim4.nim +++ b/tests/method/tmultim4.nim @@ -5,7 +5,7 @@ discard """ type Test = object of TObject -method doMethod(a: ref TObject) {.raises: [EIO].} = +method doMethod(a: ref TObject) {.base, raises: [EIO].} = quit "override" method doMethod(a: ref Test) = diff --git a/tests/method/tmultim6.nim b/tests/method/tmultim6.nim index 6c21f80d27..97ed2845ce 100644 --- a/tests/method/tmultim6.nim +++ b/tests/method/tmultim6.nim @@ -10,7 +10,7 @@ type TParticle = object of TThing a, b: int -method collide(a, b: TThing) {.inline.} = +method collide(a, b: TThing) {.base, inline.} = quit "to override!" method collide[T](a: TThing, b: TUnit[T]) {.inline.} = diff --git a/tests/method/trecmeth.nim b/tests/method/trecmeth.nim index 32f620f15f..ac0a1e977c 100644 --- a/tests/method/trecmeth.nim +++ b/tests/method/trecmeth.nim @@ -2,12 +2,12 @@ # for recursive methods works, no code is being executed type - Obj = ref object of TObject + Obj = ref object of RootObj # Mutual recursion -method alpha(x: Obj) -method beta(x: Obj) +method alpha(x: Obj) {.base.} +method beta(x: Obj) {.base.} method alpha(x: Obj) = beta(x) @@ -17,6 +17,6 @@ method beta(x: Obj) = # Simple recursion -method gamma(x: Obj) = +method gamma(x: Obj) {.base.} = gamma(x) diff --git a/todo.txt b/todo.txt index 66a9d1f0f2..1160000b0c 100644 --- a/todo.txt +++ b/todo.txt @@ -13,7 +13,6 @@ version 0.11.4 - add "all threads are blocked" detection to 'spawn' - Deprecate ``immediate`` for templates and macros -- make people annotate .anchor methods version 1.0 diff --git a/web/news.txt b/web/news.txt index 823a10dfaf..80983d33d0 100644 --- a/web/news.txt +++ b/web/news.txt @@ -50,6 +50,9 @@ News and are now deprecated and will be removed from the language. Instead you have to insert type conversions 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 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 @@ -66,9 +69,11 @@ News 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 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. @@ -81,6 +86,12 @@ News locale anymore and now take an optional ``decimalSep = '.'`` parameter. + Compiler Additions + ------------------ + + - The compiler now supports a new configuration system based on ``NimScript``. + + Language Additions ------------------