Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better effect tracking for inner routines #435

Open
Araq opened this issue Nov 7, 2021 · 3 comments
Open

Better effect tracking for inner routines #435

Araq opened this issue Nov 7, 2021 · 3 comments

Comments

@Araq
Copy link
Member

Araq commented Nov 7, 2021

Refs: status-im/nim-chronos#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

  • Less code breakage for code that uses .experimental: "strictEffects" (and this mode
    is about to become the default as it fixes a number of loopholes in Nim's effect
    system.)
  • Code ends up with fewer conservative annotations which can matter for generic code.

Downsides

  • A slightly more complex compiler and language definition.
@Araq Araq changed the title Better effect tracking for inner rountines Better effect tracking for inner routines Nov 9, 2021
@juancarlospaco
Copy link
Contributor

more complex compiler and language definition.

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

@Araq
Copy link
Member Author

Araq commented Nov 17, 2021

Hard to say, 6 with the current compiler?

@Araq
Copy link
Member Author

Araq commented Jan 1, 2022

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants