fixes #2590; methods now require a .base annotation

This commit is contained in:
Araq
2015-09-06 02:29:30 +02:00
parent d2e4d6ad82
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
sfWrittenTo* = sfBorrow # param is assigned to
sfEscapes* = sfProcvar # param escapes
sfBase* = sfDiscriminant
const
# getting ready for the future expr/stmt merge

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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