nim-lang / RFCs

A repository for your Nim proposals.
135 stars 26 forks source link

Better effect tracking for inner routines #435

Open Araq opened 2 years ago

Araq commented 2 years ago

Refs: https://github.com/status-im/nim-chronos/pull/226

The problem: Currently code like this does not compile:


{.experimental: "strictEffects".}

proc outer =
  proc inner() {.tags: [].} =
    outer()

  inner()

outer()

The compiler produces:

Error: outer() can have an unlisted effect: RootEffect

When inner is analysed for its effects the effects of outer are not yet known. The specification/manual currently does not really say what should happen: outer is not forward declared as it doesn't have to be, but the intention of the spec clearly was to cover this case via the rule (3):

  1. Every call to a proc q which has an unknown body (due to a forward declaration) is assumed to raise system.Exception unless q has an explicit raises list.

So, in order to make the example compile, outer's effects need to be annotated explicitly:


{.experimental: "strictEffects".}

proc outer {.tags: [].} =
  proc inner() {.tags: [].} =
    outer()

  inner()

outer()

While that might not be that bad, the problem here is that code like in the initial example used to work for years and only the recent bugfixes in the .experimental: "strictEffects" mode make it not compile. It's a breaking change which could be handled more smoothly if we make the compiler smarter.

Proposal: Infer the effects of inner routines in combination with outer routines

The effect inference algorithm could work on outer and inner in lock-step:

When inner calls outer it is assumed that outer has no effects and if that assumption does not turn out to be true, calls of outer need to be "patched". Or, to phrase it differently: The interference algorithm infers the effects of outer by also following inner routines directly, treating them much like templates.

Benefits

Downsides

juancarlospaco commented 2 years ago

more complex compiler and language definition.

How much more complex?, like in a scale of 0 ~ 10.

Araq commented 2 years ago

Hard to say, 6 with the current compiler?

Araq commented 2 years ago

Another example of the same sort of problem:


{.experimental: "strictEffects".}

type
  Foo = object
    bar: seq[Foo]

proc `==`(a, b: Foo): bool {.noSideEffect.} =
                            # ^ explicit annotation required
  a.bar == b.bar