Merge remote-tracking branch 'origin/concepts-rebased' into araq

This commit is contained in:
Andreas Rumpf 2017-03-31 16:40:21 +02:00
commit 529609f7ed
47 changed files with 2038 additions and 345 deletions

15
tests/concepts/matrix.nim Normal file
View file

@ -0,0 +1,15 @@
type
Matrix*[M, N: static[int]; T] = object
data: array[M*N, T]
proc `[]`*(M: Matrix; m, n: int): M.T =
M.data[m * M.N + n]
proc `[]=`*(M: var Matrix; m, n: int; v: M.T) =
M.data[m * M.N + n] = v
# Adapt the Matrix type to the concept's requirements
template Rows*(M: type Matrix): expr = M.M
template Cols*(M: type Matrix): expr = M.N
template ValueType*(M: type Matrix): typedesc = M.T

View file

@ -0,0 +1,28 @@
import typetraits
type
AnyMatrix*[R, C: static[int]; T] = concept m, var mvar, type M
M.ValueType is T
M.Rows == R
M.Cols == C
m[int, int] is T
mvar[int, int] = T
type TransposedType = stripGenericParams(M)[C, R, T]
AnySquareMatrix*[N: static[int], T] = AnyMatrix[N, N, T]
AnyTransform3D* = AnyMatrix[4, 4, float]
proc transposed*(m: AnyMatrix): m.TransposedType =
for r in 0 .. <m.R:
for c in 0 .. <m.C:
result[r, c] = m[c, r]
proc determinant*(m: AnySquareMatrix): int =
return 0
proc setPerspectiveProjection*(m: AnyTransform3D) =
discard

21
tests/concepts/t1128.nim Normal file
View file

@ -0,0 +1,21 @@
discard """
output: "true\ntrue"
"""
type
TFooContainer[T] = object
TContainer[T] = generic var c
foo(c, T)
proc foo[T](c: var TFooContainer[T], val: T) =
discard
proc bar(c: var TContainer) =
discard
var fooContainer: TFooContainer[int]
echo fooContainer is TFooContainer # true.
echo fooContainer is TFooContainer[int] # true.
fooContainer.bar()

41
tests/concepts/t3330.nim Normal file
View file

@ -0,0 +1,41 @@
discard """
errormsg: "type mismatch: got (Bar[system.int])"
nimout: '''
t3330.nim(40, 4) Error: type mismatch: got (Bar[system.int])
but expected one of:
proc test(foo: Foo[int])
t3330.nim(25, 8) Hint: Non-matching candidates for add(k, string, T)
proc add[T](x: var seq[T]; y: T)
proc add(result: var string; x: float)
proc add(x: var string; y: string)
proc add(x: var string; y: cstring)
proc add(x: var string; y: char)
proc add(result: var string; x: int64)
proc add[T](x: var seq[T]; y: openArray[T])
t3330.nim(25, 8) template/generic instantiation from here
t3330.nim(32, 6) Foo: 'bar.value' cannot be assigned to
t3330.nim(25, 8) template/generic instantiation from here
t3330.nim(33, 6) Foo: 'bar.x' cannot be assigned to
'''
"""
type
Foo[T] = concept k
add(k, string, T)
Bar[T] = object
value: T
x: string
proc add[T](bar: Bar[T], x: string, val: T) =
bar.value = val
bar.x = x
proc test(foo: Foo[int]) =
foo.add("test", 42)
echo(foo.x)
var bar = Bar[int]()
bar.test()

32
tests/concepts/t976.nim Normal file
View file

@ -0,0 +1,32 @@
import macros
type
int1 = distinct int
int2 = distinct int
int1g = concept x
x is int1
int2g = concept x
x is int2
proc take[T: int1g](value: int1) =
when T is int2:
static: error("killed in take(int1)")
proc take[T: int2g](vale: int2) =
when T is int1:
static: error("killed in take(int2)")
var i1: int1 = 1.int1
var i2: int2 = 2.int2
take[int1](i1)
take[int2](i2)
template reject(e) =
static: assert(not compiles(e))
reject take[string](i2)
reject take[int1](i2)

View file

@ -0,0 +1,69 @@
discard """
output: '''x as ParameterizedType[T]
x as ParameterizedType[T]
x as ParameterizedType[T]
x as ParameterizedType
x as ParameterizedType
x as CustomTypeClass'''
"""
type ParameterizedType[T] = object
type CustomTypeClass = concept
true
# 3 competing procs
proc a[T](x: ParameterizedType[T]) =
echo "x as ParameterizedType[T]"
proc a(x: ParameterizedType) =
echo "x as ParameterizedType"
proc a(x: CustomTypeClass) =
echo "x as CustomTypeClass"
# the same procs in different order
proc b(x: ParameterizedType) =
echo "x as ParameterizedType"
proc b(x: CustomTypeClass) =
echo "x as CustomTypeClass"
proc b[T](x: ParameterizedType[T]) =
echo "x as ParameterizedType[T]"
# and yet another order
proc c(x: CustomTypeClass) =
echo "x as CustomTypeClass"
proc c(x: ParameterizedType) =
echo "x as ParameterizedType"
proc c[T](x: ParameterizedType[T]) =
echo "x as ParameterizedType[T]"
# remove the most specific one
proc d(x: ParameterizedType) =
echo "x as ParameterizedType"
proc d(x: CustomTypeClass) =
echo "x as CustomTypeClass"
# then shuffle the order again
proc e(x: CustomTypeClass) =
echo "x as CustomTypeClass"
proc e(x: ParameterizedType) =
echo "x as ParameterizedType"
# the least specific one is a match
proc f(x: CustomTypeClass) =
echo "x as CustomTypeClass"
a(ParameterizedType[int]())
b(ParameterizedType[int]())
c(ParameterizedType[int]())
d(ParameterizedType[int]())
e(ParameterizedType[int]())
f(ParameterizedType[int]())

120
tests/concepts/texplain.nim Normal file
View file

@ -0,0 +1,120 @@
discard """
cmd: "nim c --verbosity:0 --colors:off $file"
nimout: '''
texplain.nim(99, 10) Hint: Non-matching candidates for e(y)
proc e(i: int): int
texplain.nim(102, 7) Hint: Non-matching candidates for e(10)
proc e(o: ExplainedConcept): int
texplain.nim(65, 6) ExplainedConcept: undeclared field: 'foo'
texplain.nim(65, 6) ExplainedConcept: undeclared field: '.'
texplain.nim(65, 6) ExplainedConcept: expression '.' cannot be called
texplain.nim(65, 5) ExplainedConcept: type class predicate failed
texplain.nim(66, 6) ExplainedConcept: undeclared field: 'bar'
texplain.nim(66, 6) ExplainedConcept: undeclared field: '.'
texplain.nim(66, 6) ExplainedConcept: expression '.' cannot be called
texplain.nim(65, 5) ExplainedConcept: type class predicate failed
texplain.nim(105, 10) Hint: Non-matching candidates for e(10)
proc e(o: ExplainedConcept): int
texplain.nim(65, 6) ExplainedConcept: undeclared field: 'foo'
texplain.nim(65, 6) ExplainedConcept: undeclared field: '.'
texplain.nim(65, 6) ExplainedConcept: expression '.' cannot be called
texplain.nim(65, 5) ExplainedConcept: type class predicate failed
texplain.nim(66, 6) ExplainedConcept: undeclared field: 'bar'
texplain.nim(66, 6) ExplainedConcept: undeclared field: '.'
texplain.nim(66, 6) ExplainedConcept: expression '.' cannot be called
texplain.nim(65, 5) ExplainedConcept: type class predicate failed
texplain.nim(109, 20) Error: type mismatch: got (NonMatchingType)
but expected one of:
proc e(o: ExplainedConcept): int
texplain.nim(65, 5) ExplainedConcept: type class predicate failed
proc e(i: int): int
texplain.nim(110, 20) Error: type mismatch: got (NonMatchingType)
but expected one of:
proc r(o: RegularConcept): int
texplain.nim(69, 5) RegularConcept: type class predicate failed
proc r[T](a: SomeNumber; b: T; c: auto)
proc r(i: string): int
texplain.nim(111, 20) Hint: Non-matching candidates for r(y)
proc r[T](a: SomeNumber; b: T; c: auto)
proc r(i: string): int
texplain.nim(119, 2) Error: type mismatch: got (MatchingType)
but expected one of:
proc f(o: NestedConcept)
texplain.nim(69, 6) RegularConcept: undeclared field: 'foo'
texplain.nim(69, 6) RegularConcept: undeclared field: '.'
texplain.nim(69, 6) RegularConcept: expression '.' cannot be called
texplain.nim(69, 5) RegularConcept: type class predicate failed
texplain.nim(70, 6) RegularConcept: undeclared field: 'bar'
texplain.nim(70, 6) RegularConcept: undeclared field: '.'
texplain.nim(70, 6) RegularConcept: expression '.' cannot be called
texplain.nim(69, 5) RegularConcept: type class predicate failed
texplain.nim(73, 5) NestedConcept: type class predicate failed
'''
line: 119
errormsg: "type mismatch: got (MatchingType)"
"""
type
ExplainedConcept {.explain.} = concept o
o.foo is int
o.bar is string
RegularConcept = concept o
o.foo is int
o.bar is string
NestedConcept = concept o
o.foo is RegularConcept
NonMatchingType = object
foo: int
bar: int
MatchingType = object
foo: int
bar: string
proc e(o: ExplainedConcept): int = 1
proc e(i: int): int = i
proc r[T](a: SomeNumber, b: T, c: auto) = discard
proc r(o: RegularConcept): int = 1
proc r(i: string): int = 1
proc f(o: NestedConcept) = discard
var n = NonMatchingType(foo: 10, bar: 20)
var y = MatchingType(foo: 10, bar: "bar")
# no diagnostic here:
discard e(y)
# explain that e(int) doesn't match
discard e(y) {.explain.}
# explain that e(ExplainedConcept) doesn't match
echo(e(10) {.explain.}, 20)
# explain that e(ExplainedConcept) doesn't again
discard e(10)
static:
# provide diagnostics why the compile block failed
assert(compiles(e(n)) {.explain.} == false)
assert(compiles(r(n)) {.explain.} == false)
assert(compiles(r(y)) {.explain.} == true)
# these should not produce any output
assert(compiles(r(10)) == false)
assert(compiles(e(10)) == true)
# finally, provide multiple nested explanations for failed matching
# of regular concepts, even when the explain pragma is not used
f(y)

View file

@ -0,0 +1,102 @@
discard """
output: '''10
10
nil
1'''
msg: '''
K=string V=int
K=int64 V=string
K=int V=int
'''
"""
import tables, typetraits
template ok(check) = assert check
template no(check) = assert(not check)
type
Enumerable[T] = concept e
for v in e:
v is T
Map[K, V] = concept m, var mvar
m[K] is V
mvar[K] = V
m.contains(K) is bool
m.valuesSeq is Enumerable[V]
TreeMap[K, V] = object
root: int
SparseSeq = object
data: seq[int]
JudyArray = object
data: SparseSeq
static:
ok seq[int] is Enumerable[int]
ok seq[string] is Enumerable
ok seq[int] is Enumerable[SomeNumber]
ok SparseSeq.data is Enumerable
no seq[string] is Enumerable[int]
no int is Enumerable
no int is Enumerable[int]
# Complete the map concept implementation for the Table type
proc valuesSeq[K, V](t: Table[K, V]): seq[V] =
result = @[]
for k, v in t:
result.add v
# Map concept inplementation for TreeMap
proc valuesSeq(t: TreeMap): array[1, TreeMap.V] =
var v: t.V
result = [v]
proc contains[K, V](t: TreeMap[K, V], key: K): bool = true
proc `[]=`[K, V](t: var TreeMap[K, V], key: K, val: V) = discard
proc `[]`(t: TreeMap, key: t.K): TreeMap.V = discard
# Map concept implementation for the non-generic JudyArray
proc valuesSeq(j: JudyArray): SparseSeq = j.data
proc contains(t: JudyArray, key: int): bool = true
proc `[]=`(t: var JudyArray, key, val: int) = discard
proc `[]`(t: JudyArray, key: int): int = discard
iterator items(s: SparseSeq): int =
for i in s.data: yield i
# Generic proc defined over map
proc getFirstValue[K,V](m : Map[K,V]): V =
static: echo "K=", K.name, " V=", V.name
for i in m.valuesSeq:
return i
raise newException(RangeError, "no values")
proc useConceptProcInGeneric[K, V](t: Table[K, V]): V =
return t.getFirstValue
var t = initTable[string, int]()
t["test"] = 10
echo t.getFirstValue
echo t.useConceptProcInGeneric
var tm = TreeMap[int64, string](root: 0)
echo getFirstValue(tm)
var j = JudyArray(data: SparseSeq(data: @[1, 2, 3]))
echo getFirstValue(j)
static:
ok Table[int, float] is Map
ok Table[int, string] is Map[SomeNumber, string]
no JudyArray is Map[string, int]

View file

@ -0,0 +1,81 @@
discard """
output: "0\n0\n0"
msg: '''
R=3 C=3 TE=9 FF=14 FC=20 T=int
R=3 C=3 T=int
'''
"""
import typetraits
template ok(x) = assert x
template no(x) = assert(not x)
const C = 10
type
Matrix[Rows, Cols, TotalElements, FromFoo, FromConst: static[int]; T] = concept m, var mvar, type M
M.M == Rows
Cols == M.N
M.T is T
m[int, int] is T
mvar[int, int] = T
FromConst == C * 2
# more complicated static param inference cases
m.data is array[TotalElements, T]
m.foo(array[0..FromFoo, type m[int, 10]])
MyMatrix[M, K: static[int]; T] = object
data: array[M*K, T]
# adaptor for the concept's non-matching expectations
template N(M: type MyMatrix): expr = M.K
proc `[]`(m: MyMatrix; r, c: int): m.T =
m.data[r * m.K + c]
proc `[]=`(m: var MyMatrix; r, c: int, v: m.T) =
m.data[r * m.K + c] = v
proc foo(x: MyMatrix, arr: array[15, x.T]) = discard
proc genericMatrixProc[R, C, TE, FF, FC, T](m: Matrix[R, C, TE, FF, FC, T]): T =
static:
echo "R=", R, " C=", C, " TE=", TE, " FF=", FF, " FC=", FC, " T=", T.name
m[0, 0]
proc implicitMatrixProc(m: Matrix): m.T =
static:
echo "R=", m.Rows,
" C=", m.Cols,
# XXX: fix these
#" TE=", m.TotalElements,
#" FF=", m.FromFoo,
#" FC=", m.FromConst,
" T=", m.T.name
m[0, 0]
proc myMatrixProc(x: MyMatrix): MyMatrix.T = genericMatrixProc(x)
var x: MyMatrix[3, 3, int]
static:
# ok x is Matrix
ok x is Matrix[3, 3, 9, 14, 20, int]
no x is Matrix[3, 3, 8, 15, 20, int]
no x is Matrix[3, 3, 9, 10, 20, int]
no x is Matrix[3, 3, 9, 15, 21, int]
no x is Matrix[3, 3, 9, 15, 20, float]
no x is Matrix[4, 3, 9, 15, 20, int]
no x is Matrix[3, 4, 9, 15, 20, int]
echo x.myMatrixProc
echo x.genericMatrixProc
echo x.implicitMatrixProc

View file

@ -0,0 +1,31 @@
discard """
output: "0"
"""
import matrix, matrixalgo
import typetraits # XXX: this should be removed
var m: Matrix[3, 3, int]
var projectionMatrix: Matrix[4, 4, float]
echo m.transposed.determinant
setPerspectiveProjection projectionMatrix
template ok(x) = assert x
template no(x) = assert(not x)
static:
ok projectionMatrix is AnyTransform3D
no m is AnyTransform3D
type SquareStringMatrix = Matrix[5, 5, string]
ok SquareStringMatrix is AnyMatrix
ok SquareStringMatrix is AnySquareMatrix
no SquareStringMatrix is AnyTransform3D
ok Matrix[5, 10, int] is AnyMatrix
no Matrix[7, 15, float] is AnySquareMatrix
no Matrix[4, 4, int] is AnyTransform3D

View file

@ -0,0 +1,100 @@
discard """
output: '''true
true
true
true
p has been called.
p has been called.
implicit generic
generic
false
true
-1'''
"""
# https://github.com/nim-lang/Nim/issues/1147
type TTest = object
vals: seq[int]
proc add*(self: var TTest, val: int) =
self.vals.add(val)
type CAddable = concept x
x[].add(int)
echo((ref TTest) is CAddable) # true
# https://github.com/nim-lang/Nim/issues/1570
type ConcretePointOfFloat = object
x, y: float
type ConcretePoint[Value] = object
x, y: Value
type AbstractPointOfFloat = generic p
p.x is float and p.y is float
let p1 = ConcretePointOfFloat(x: 0, y: 0)
let p2 = ConcretePoint[float](x: 0, y: 0)
echo p1 is AbstractPointOfFloat # true
echo p2 is AbstractPointOfFloat # true
echo p2.x is float and p2.y is float # true
# https://github.com/nim-lang/Nim/issues/2018
type ProtocolFollower = generic
true # not a particularly involved protocol
type ImplementorA = object
type ImplementorB = object
proc p[A: ProtocolFollower, B: ProtocolFollower](a: A, b: B) =
echo "p has been called."
p(ImplementorA(), ImplementorA())
p(ImplementorA(), ImplementorB())
# https://github.com/nim-lang/Nim/issues/2423
proc put*[T](c: seq[T], x: T) = echo "generic"
proc put*(c: seq) = echo "implicit generic"
type
Container[T] = concept c
put(c)
put(c, T)
proc c1(x: Container) = echo "implicit generic"
c1(@[1])
proc c2[T](x: Container[T]) = echo "generic"
c2(@[1])
# https://github.com/nim-lang/Nim/issues/2882
type
Paper = object
name: string
Bendable = concept x
bend(x is Bendable)
proc bend(p: Paper): Paper = Paper(name: "bent-" & p.name)
var paper = Paper(name: "red")
echo paper is Bendable
type
A = concept self
size(self) is int
B = object
proc size(self: B): int =
return -1
proc size(self: A): int =
return 0
let b = B()
echo b is A
echo b.size()

View file

@ -0,0 +1,63 @@
discard """
output: "20\n10"
msg: '''
INFERRED int
VALUE TYPE int
VALUE TYPE NAME INT
IMPLICIT INFERRED int int
IMPLICIT VALUE TYPE int int
IMPLICIT VALUE TYPE NAME INT INT
'''
"""
import typetraits, strutils
template reject(e: expr) =
static: assert(not compiles(e))
type
ArrayStack = object
data: seq[int]
proc push(s: var ArrayStack, item: int) =
s.data.add item
proc pop(s: var ArrayStack): int =
return s.data.pop()
type
Stack[T] = concept var s
s.push(T)
s.pop() is T
type ValueType = T
const ValueTypeName = T.name.toUpper
proc genericAlgorithm[T](s: var Stack[T], y: T) =
static:
echo "INFERRED ", T.name
echo "VALUE TYPE ", s.ValueType.name
echo "VALUE TYPE NAME ", s.ValueTypeName
s.push(y)
echo s.pop
proc implicitGeneric(s: var Stack): auto =
static:
echo "IMPLICIT INFERRED ", s.T.name, " ", Stack.T.name
echo "IMPLICIT VALUE TYPE ", s.ValueType.name, " ", Stack.ValueType.name
echo "IMPLICIT VALUE TYPE NAME ", s.ValueTypeName, " ", Stack.ValueTypeName
return s.pop()
var s = ArrayStack(data: @[])
s.push 10
s.genericAlgorithm 20
echo s.implicitGeneric
reject s.genericAlgorithm "x"
reject s.genericAlgorithm 1.0
reject "str".implicitGeneric
reject implicitGeneric(10)

View file

@ -2,20 +2,22 @@ discard """
output: '''Sortable
Sortable
Container
true
true
false
false
false
TObj
int
'''
"""
import typetraits
template reject(expr) = assert(not compiles(x))
type
TObj = object
x: int
JSonValue = object
val: string
Sortable = concept x, y
(x < y) is bool
@ -23,7 +25,7 @@ type
C.len is Ordinal
for v in items(C):
v.type is tuple|object
proc foo(c: ObjectContainer) =
echo "Container"
@ -36,33 +38,62 @@ foo(@[TObj(x: 10), TObj(x: 20)])
proc intval(x: int): int = 10
# check real and virtual fields
type
TFoo = concept T
T.x
y(T)
intval T.y
let z = intval(T.y)
TFoo = concept o, type T, ref r, var v, ptr p, static s
o.x
y(o) is int
var str: string
var intref: ref int
refproc(ref T, ref int)
varproc(var T)
ptrproc(ptr T, str)
staticproc(static[T])
typeproc T
T.typeproc
typeproc o.type
o.type.typeproc
o.to(type string)
o.to(type JsonValue)
refproc(r, intref)
varproc(v)
p.ptrproc(string)
staticproc s
typeproc(T)
const TypeName = T.name
type MappedType = type(o.y)
intval y(o)
let z = intval(o.y)
static:
assert T.name.len == 4
reject o.name
reject o.typeproc
reject staticproc(o)
reject o.varproc
reject T.staticproc
reject p.staticproc
proc y(x: TObj): int = 10
proc testFoo(x: TFoo) = discard
proc varproc(x: var TObj) = discard
proc refproc(x: ref TObj, y: ref int) = discard
proc ptrproc(x: ptr TObj, y: string) = discard
proc staticproc(x: static[TObj]) = discard
proc typeproc(t: type TObj) = discard
proc to(x: TObj, t: type string) = discard
proc to(x: TObj, t: type JSonValue) = discard
proc testFoo(x: TFoo) =
echo x.TypeName
echo x.MappedType.name
testFoo(TObj(x: 10))
type
Matrix[Rows, Cols: static[int]; T] = concept M
M.M == Rows
M.N == Cols
M.T is T
MyMatrix[M, N: static[int]; T] = object
data: array[M*N, T]
var x: MyMatrix[3, 3, int]
echo x is Matrix
echo x is Matrix[3, 3, int]
echo x is Matrix[3, 3, float]
echo x is Matrix[4, 3, int]
echo x is Matrix[3, 4, int]

View file

@ -0,0 +1,15 @@
type VectorSpace[K] = concept x, y
x + y is type(x)
zero(type(x)) is type(x)
-x is type(x)
x - y is type(x)
var k: K
k * x is type(x)
proc zero(T: typedesc): T = 0
static:
assert float is VectorSpace[float]
# assert float is VectorSpace[int]
# assert int is VectorSpace

View file

@ -0,0 +1,29 @@
discard """
output: '''string
int
(int, string)
'''
"""
import typetraits
type
Foo[T, U] = object
x: T
y: U
proc bar[T](a: T): T.U =
echo result.type.name
proc bas(x: auto): x.T =
echo result.type.name
proc baz(x: Foo): (Foo.T, x.U) =
echo result.type.name
var
f: Foo[int, string]
x = bar f
z = bas f
y = baz f

View file

@ -1,8 +1,8 @@
discard """
line: 28
nimout: '''tgc_unsafe2.nim(22, 5) Warning: 'trick' is not GC-safe as it accesses 'global' which is a global using GC'ed memory
tgc_unsafe2.nim(26, 5) Warning: 'track' is not GC-safe as it calls 'trick'
tgc_unsafe2.nim(28, 5) Error: 'consumer' is not GC-safe as it calls 'track'
nimout: '''tgc_unsafe2.nim(22, 6) Warning: 'trick' is not GC-safe as it accesses 'global' which is a global using GC'ed memory [GcUnsafe2]
tgc_unsafe2.nim(26, 6) Warning: 'track' is not GC-safe as it calls 'trick' [GcUnsafe2]
tgc_unsafe2.nim(28, 6) Error: 'consumer' is not GC-safe as it calls 'track'
'''
errormsg: "'consumer' is not GC-safe as it calls 'track'"
"""

View file

@ -63,6 +63,8 @@ let
var targets = {low(TTarget)..high(TTarget)}
proc normalizeMsg(s: string): string = s.strip.replace("\C\L", "\L")
proc callCompiler(cmdTemplate, filename, options: string,
target: TTarget): TSpec =
let c = parseCmdLine(cmdTemplate % ["target", targetToCmd[target],
@ -184,6 +186,8 @@ proc addResult(r: var TResults, test: TTest,
proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest) =
if strip(expected.msg) notin strip(given.msg):
r.addResult(test, expected.msg, given.msg, reMsgsDiffer)
elif expected.nimout.len > 0 and expected.nimout.normalizeMsg notin given.nimout.normalizeMsg:
r.addResult(test, expected.nimout, given.nimout, reMsgsDiffer)
elif expected.tfile == "" and extractFilename(expected.file) != extractFilename(given.file) and
"internal error:" notin expected.msg:
r.addResult(test, expected.file, given.file, reFilesDiffer)