-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Description
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 effectsExample 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)]) # errorEDIT: 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 hitsError: '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
- devel 1.5.1 a32acc3
- related to
funcdoesn't check for side effects in proc parameters #16303