Skip to content

strictFuncs/views design issue #16305

@timotheecour

Description

@timotheecour

strictFuncs/views seems to have a design issue, which prevents algorithm.sorted and many other procs from being func as I noticed when I started investigating the initial CI failures in in #16304 (comment)

What's worse is that, when using generic func's, the issue only manifests itself when the func is instantiated with types containing ref, so that generic code that's seemingly valid when used in some project may fail when used in other contexts, causing regressions when proc's are changed to func's in a seemingly innocuous way (see example 6 for an example of such regression).

Example 1

with --experimental:strictFuncs this gives: Error: 'sortedFake2' can have side effects

  func sortedFake1[T](a: openArray[T]): seq[T] =
    for i in 0 .. a.high: result.add a[i]
  func sortedFake2[T](a: openArray[T]): seq[T] =
    result = newSeq[T](a.len)
    for i in 0 .. a.high: result[i] = a[i]
  type Foo1 = object
  type Foo2 = ref object
  block:
    let a1 = sortedFake1([Foo1()]) # ok
    let a2 = sortedFake1([Foo2()]) # ok
  block:
    let a1 = sortedFake2([Foo1()]) # ok
    let a2 = sortedFake2([Foo2()]) # error: Error: 'sortedFake2' can have side effects

Example 2

after reduction of the CI error I saw in #16304 (comment):

when true: # D20201209T112151:here gitissue
  type Foo = ref object
  proc fnSideEffect(n: Foo) = echo 1
  func aux(b: var Foo, bar: proc (x: Foo)) = discard
  func foo(a: Foo, bar: proc(x: Foo)) =
    bar(a) # ok
    var s = a
    aux(s, bar) # bug: gives error here
  var c = Foo()
  foo(c, fnSideEffect)

Example 3

this works fine, which is a reduction of the example in #16303:

when true:
  type Foo = ref object
  proc fnSideEffect(a: Foo) = echo 1
  func foo(a: Foo, bar: proc(x: Foo)) = bar(a)
  var a = Foo()
  foo(a, fnSideEffect)

Example 4

reduction of Example 2:

when true:
  type Foo = ref object
  func aux(b: var Foo) = discard
  func foo(a: Foo) =
    var s = a
    aux(s)
  foo(Foo())

also gives:

t11479.nim(158, 8) Error: 'foo' can have side effects
an object reachable from 'a' is potentially mutated
t11479.nim(160, 9) the mutation is here
t11479.nim(159, 9) is the statement that connected the mutation to the parameter
    func foo(a: Foo) =

Example 5

shows the kind of regressions you get:
before #16293

nim r --experimental:strictFuncs main # ditto with views
ok

after #16293

nim r --experimental:strictFuncs main # ditto with views
Error: 'zip' can have side effects
when true:
  import std/sequtils
  type Foo = ref object
    x: int
  let a1 = zip(@[1,2], @[1,2]) # ok
  let a2 = zip(@[Foo(x: 1)], @[Foo(x: 2)]) # error

EDIT: likewise with other procs, eg sequtils.map, eg:

when true:
  import std/sequtils
  func tap[T](a: T): T = a
  type Foo = ref object
    x: int
  let b = map([Foo(x: 1), Foo(x: 2)], tap)

Error: 'map' can have side effects
an object reachable from 's' is potentially mutated

Example 6

this valid code stops to work after #16293:

  • with --experimental:views, it hits Error: 'zip' can have side effects
  • without, it hits: Error: invalid type: 'openArray[int]' for let
when true:
  proc test(x: openArray[int]) =
    let y: openArray[int] = toOpenArray(x, 0, 0)
  import std/sequtils
  type Foo = ref object
    x: int
  let a1 = zip(@[1,2], @[1,2])
  let a2 = zip(@[Foo(x: 1)], @[Foo(x: 2)])

ditto with algorithm.map:

when true:
  {.experimental: "views".}
  import std/sequtils
  func tap[T](a: T): T = a
  type Foo = ref object
    x: int
  proc test[T](x: openArray[T]) =
    let y = toOpenArray(x, 0, 0)
    let y2 = y.map(tap) #  Error: 'map' can have side effects
  test(@[Foo(x: 1), Foo(x: 2)])

Example 7

see #18065 (comment), because of --experimental:strictFuncs, a cast was used:

 {.cast(noSideEffect).}:
    result.mimes = mimes.newOrderedTable()

which is IMO a sign of defeat; and on top of that it masks actual side effects for code not built with --experimental:strictFuncs

Additional Information

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions