From c83e48ce785d01d02d2fd4207997d81bb08308b1 Mon Sep 17 00:00:00 2001 From: metagn Date: Sat, 31 Aug 2024 19:53:13 +0300 Subject: [PATCH] stricter `var` type conversions, subranges under preview define closes #24032 --- changelog.md | 15 +++++++++++++++ compiler/sigmatch.nim | 17 +++++++++++++++-- lib/pure/json.nim | 20 ++++++++++++++++++++ lib/pure/unicode.nim | 2 +- tests/errmsgs/t22097.nim | 4 ++-- tests/int/twrongexplicitvarconv.nim | 16 ++++++++++++++++ tests/int/twrongvarconv.nim | 9 +++++++++ tests/iter/titer12.nim | 2 +- tests/overflow/trangechecks.nim | 4 ++-- tests/range/twrongvarconv.nim | 18 ++++++++++++++++++ 10 files changed, 99 insertions(+), 8 deletions(-) create mode 100644 tests/int/twrongexplicitvarconv.nim create mode 100644 tests/int/twrongvarconv.nim create mode 100644 tests/range/twrongvarconv.nim diff --git a/changelog.md b/changelog.md index 541bbd6eb108..cdb9cb70f013 100644 --- a/changelog.md +++ b/changelog.md @@ -28,6 +28,21 @@ errors for ambiguous type symbols, and macros operating on generic proc AST may encounter symchoice nodes instead of the arbitrarily resolved type symbol nodes. +- With `-d:nimPreviewStrictVarRange`, variables of range types do not match + `var` parameters of their base type. This is to prevent implicit operations + that can cause values to escape their range, i.e.: + + ```nim + type Foo = range[0..5] + var foo: Foo = 5 + proc double(x: var int) = + x = x * 2 + double(foo) + echo foo # 10 + ``` + + will give a type mismatch error. + ## Standard library additions and changes [//]: # "Changes:" diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 89b66b52473d..7dcd2fb791f1 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1017,9 +1017,22 @@ proc inferStaticsInRange(c: var TCandidate, doInferStatic(lowerBound, getInt(upperBound) + 1 - lengthOrd(c.c.config, concrete)) template subtypeCheck() = - if result <= isSubrange and f.last.skipTypes(abstractInst).kind in { - tyRef, tyPtr, tyVar, tyLent, tyOwned}: + case result + of isIntConv: result = isNone + of isSubrange: + if c.c.config.isDefined("nimPreviewStrictVarRange"): + result = isNone + of isConvertible: + if f.last.skipTypes(abstractInst).kind != tyOpenArray: + # exclude var openarray which compiler supports + result = isNone + of isSubtype: + if f.last.skipTypes(abstractInst).kind in { + tyRef, tyPtr, tyVar, tyLent, tyOwned}: + # compiler can't handle subtype conversions with pointer indirection + result = isNone + else: discard proc isCovariantPtr(c: var TCandidate, f, a: PType): bool = # this proc is always called for a pair of matching types diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 53fa7553a422..4ab2c75f50e2 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -1094,6 +1094,8 @@ proc initFromJson[T](dst: var OrderedTable[string, T]; jsonNode: JsonNode; jsonP proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: var string) proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: var string) proc initFromJson[T: distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string) +when defined(nimPreviewStrictVarRange): + proc initFromJson[T: range](dst: var T; jsonNode: JsonNode; jsonPath: var string) proc initFromJson[T: object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: var string) # initFromJson definitions @@ -1224,6 +1226,24 @@ macro assignDistinctImpl[T: distinct](dst: var T;jsonNode: JsonNode; jsonPath: v proc initFromJson[T: distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string) = assignDistinctImpl(dst, jsonNode, jsonPath) +when defined(nimPreviewStrictVarRange): + # doesn't work without #24037 + macro assignRangeImpl[T: range](dst: var T;jsonNode: JsonNode; jsonPath: var string) = + let typImpl = getTypeImpl(dst) + assert typImpl.kind == nnkBracketExpr and typImpl[0].eqIdent"range" + let lowNode = + if typImpl[^1].kind == nnkInfix: + typImpl[^1][1] + else: + typImpl[1] + let baseTyp = getType(lowNode) + + result = quote do: + initFromJson(`baseTyp`(`dst`), `jsonNode`, `jsonPath`) + + proc initFromJson[T: range](dst: var T; jsonNode: JsonNode; jsonPath: var string) = + assignRangeImpl(dst, jsonNode, jsonPath) + proc detectIncompatibleType(typeExpr, lineinfoNode: NimNode) = if typeExpr.kind == nnkTupleConstr: error("Use a named tuple instead of: " & typeExpr.repr, lineinfoNode) diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index 8cbe117bb70a..d82d690ca693 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -331,7 +331,7 @@ proc runeOffset*(s: openArray[char], pos: Natural, start: Natural = 0): int = i = 0 o = start while i < pos: - o += runeLenAt(s, o) + inc o, runeLenAt(s, o) if o >= s.len: return -1 inc i diff --git a/tests/errmsgs/t22097.nim b/tests/errmsgs/t22097.nim index b50db08a3971..bb24ee8d3b2d 100644 --- a/tests/errmsgs/t22097.nim +++ b/tests/errmsgs/t22097.nim @@ -1,9 +1,9 @@ discard """ - errormsg: "for a 'var' type a variable needs to be passed; but 'uint16(x)' is immutable" + errormsg: "type mismatch: got " """ proc toUInt16(x: var uint16) = discard var x = uint8(1) -toUInt16 x \ No newline at end of file +toUInt16 x diff --git a/tests/int/twrongexplicitvarconv.nim b/tests/int/twrongexplicitvarconv.nim new file mode 100644 index 000000000000..79f770e8e9a2 --- /dev/null +++ b/tests/int/twrongexplicitvarconv.nim @@ -0,0 +1,16 @@ +discard """ + action: reject + nimout: ''' + but expression 'int(a)' is immutable, not 'var' +''' +""" + +proc `++`(n: var int) = + n += 1 + +var a: int32 = 15 + +++int(a) #[tt.Error +^ type mismatch: got ]# + +echo a diff --git a/tests/int/twrongvarconv.nim b/tests/int/twrongvarconv.nim new file mode 100644 index 000000000000..db6ac2c53d3b --- /dev/null +++ b/tests/int/twrongvarconv.nim @@ -0,0 +1,9 @@ +proc `++`(n: var int) = + n += 1 + +var a: int32 = 15 + +++a #[tt.Error +^ type mismatch: got ]# + +echo a diff --git a/tests/iter/titer12.nim b/tests/iter/titer12.nim index f7fc64da4eb5..11539b18e85a 100644 --- a/tests/iter/titer12.nim +++ b/tests/iter/titer12.nim @@ -49,7 +49,7 @@ proc filter[T](it: (iterator : T), f: proc(x: T): bool): (iterator : T) = proc len[T](it : iterator : T) : Natural = for i in it(): - result += 1 + result.inc 1 proc simpleSeqIterator(s :seq[int]) : iterator : int = iterator it: int {.closure.} = diff --git a/tests/overflow/trangechecks.nim b/tests/overflow/trangechecks.nim index e48b1272b1d4..f1fcacf5429f 100644 --- a/tests/overflow/trangechecks.nim +++ b/tests/overflow/trangechecks.nim @@ -12,7 +12,7 @@ var expected: int var x: range[1..10] = 10 try: - x += 1 + inc x, 1 echo x except OverflowDefect, RangeDefect: expected += 1 @@ -27,7 +27,7 @@ except OverflowDefect, RangeDefect: x = 1 try: - x -= 1 + dec x, 1 echo x except OverflowDefect, RangeDefect: expected += 1 diff --git a/tests/range/twrongvarconv.nim b/tests/range/twrongvarconv.nim new file mode 100644 index 000000000000..4717f267934e --- /dev/null +++ b/tests/range/twrongvarconv.nim @@ -0,0 +1,18 @@ +discard """ + matrix: "-d:nimPreviewStrictVarRange" +""" + +# issue #24032 + +proc `++`(n: var int) = + n += 1 + +type + r = range[ 0..15 ] + +var a: r = 15 + +++a #[tt.Error +^ type mismatch: got ]# + +echo a