From 124a58a3e713bcaf240df8c4dd7e4380770b62d9 Mon Sep 17 00:00:00 2001 From: Bung Date: Wed, 26 Apr 2023 11:20:41 +0800 Subject: [PATCH] fix #6407 SomeFloat and SomeInteger types fails to differentiate in a generic call from other generic --- compiler/semexprs.nim | 2 +- compiler/sigmatch.nim | 70 +++++++++++++++++++++++++++++++++----- compiler/types.nim | 1 + lib/pure/json.nim | 10 +++--- lib/pure/strformat.nim | 2 +- tests/generics/t13549.nim | 10 ++++++ tests/generics/t18314.nim | 14 ++++++++ tests/generics/t4084.nim | 13 +++++++ tests/generics/t6407.nim | 12 +++++++ tests/generics/t6407_2.nim | 10 ++++++ 10 files changed, 129 insertions(+), 15 deletions(-) create mode 100644 tests/generics/t13549.nim create mode 100644 tests/generics/t18314.nim create mode 100644 tests/generics/t4084.nim create mode 100644 tests/generics/t6407.nim create mode 100644 tests/generics/t6407_2.nim diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index a55d74a24036..f6378cafb25a 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1874,7 +1874,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = rhsTyp = rhsTyp.lastSon if lhs.sym.typ.kind == tyAnything: rhsTyp = rhsTyp.skipIntLit(c.idgen) - if cmpTypes(c, lhs.typ, rhsTyp) in {isGeneric, isEqual}: + if cmpTypes(c, lhs.typ, rhsTyp) in {isGeneric, isEqual, isTypeClass}: internalAssert c.config, c.p.resultSym != nil # Make sure the type is valid for the result variable typeAllowedCheck(c, n.info, rhsTyp, skResult) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 6c119b71f375..f5ff04df6826 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -43,7 +43,9 @@ type TCandidate* = object c*: PContext exactMatches*: int # also misused to prefer iters over procs + typeClassMatches: int genericMatches: int # also misused to prefer constraints + genericConstrainMatches: int subtypeMatches: int intConvMatches: int # conversions to int are not as expensive convMatches: int @@ -182,6 +184,7 @@ proc copyCandidate(a: var TCandidate, b: TCandidate) = a.convMatches = b.convMatches a.intConvMatches = b.intConvMatches a.genericMatches = b.genericMatches + a.typeClassMatches = b.typeClassMatches a.state = b.state a.callee = b.callee a.calleeSym = b.calleeSym @@ -282,7 +285,9 @@ proc complexDisambiguation(a, b: PType): int = proc writeMatches*(c: TCandidate) = echo "Candidate '", c.calleeSym.name.s, "' at ", c.c.config $ c.calleeSym.info echo " exact matches: ", c.exactMatches + echo " type class matches: ", c.typeClassMatches echo " generic matches: ", c.genericMatches + echo " generic constrain matches: ", c.genericConstrainMatches echo " subtype matches: ", c.subtypeMatches echo " intconv matches: ", c.intConvMatches echo " conv matches: ", c.convMatches @@ -291,7 +296,19 @@ proc writeMatches*(c: TCandidate) = proc cmpCandidates*(a, b: TCandidate): int = result = a.exactMatches - b.exactMatches if result != 0: return - result = a.genericMatches - b.genericMatches + result = a.typeClassMatches - b.typeClassMatches + if result != 0: return + let ag = a.genericMatches + a.genericConstrainMatches + let bg = b.genericMatches + b.genericConstrainMatches + result = ag - bg + if result != 0: return + result = a.subtypeMatches - b.subtypeMatches + if result != 0: return + result = a.typeClassMatches - b.typeClassMatches + if result != 0: return + let ag = a.genericMatches + a.genericConstrainMatches + let bg = b.genericMatches + b.genericConstrainMatches + result = ag - bg if result != 0: return result = a.subtypeMatches - b.subtypeMatches if result != 0: return @@ -1653,7 +1670,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, target.callConv != effectiveArgType.callConv: return isNone put(c, f, a) - return isGeneric + return isTypeClass else: return isNone @@ -2033,7 +2050,8 @@ proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) = case r of isConvertible, isIntConv: inc(m.convMatches, convMatch) of isSubtype, isSubrange: inc(m.subtypeMatches) - of isGeneric, isInferred, isBothMetaConvertible: inc(m.genericMatches) + of isTypeClass: inc(m.typeClassMatches, 2) + of isGeneric, isInferred, isBothMetaConvertible: inc(m.genericMatches, 2) of isFromIntLit: inc(m.intConvMatches, 256) of isInferredConvertible: inc(m.convMatches) @@ -2173,9 +2191,12 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, inc(m.convMatches) result = implicitConv(nkHiddenStdConv, f, result, m, c) else: - inc(m.genericMatches) - of isGeneric: - inc(m.genericMatches) + inc(m.genericMatches, 2) + of isGeneric, isTypeClass: + if r == isGeneric: + inc(m.genericMatches, 2) + else: + inc(m.typeClassMatches, 2) if arg.typ == nil: result = arg elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple or @@ -2204,7 +2225,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, of isNone: # do not do this in ``typeRel`` as it then can't infer T in ``ref T``: if a.kind in {tyProxy, tyUnknown}: - inc(m.genericMatches) + inc(m.genericMatches, 2) m.fauxMatch = a.kind return arg elif a.kind == tyVoid and f.matchesVoidProc and argOrig.kind == nkStmtList: @@ -2216,7 +2237,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, params = nkFormalParams.newTree(p.emptyNode), name = p.emptyNode, pattern = p.emptyNode, genericParams = p.emptyNode, pragmas = p.emptyNode, exceptions = p.emptyNode), {}) if f.kind == tyBuiltInTypeClass: - inc m.genericMatches + inc m.typeClassMatches, 2 put(m, f, lifted.typ) inc m.convMatches return implicitConv(nkHiddenStdConv, f, lifted, m, c) @@ -2395,6 +2416,36 @@ proc findFirstArgBlock(m: var TCandidate, n: PNode): int = result = a2 else: break +proc checkGenericConstraint(m: var TCandidate; t: PType; typ: PType): bool = + if typ == nil: return + case t.kind + of tyOr: + for s in t.sons: + if m.checkGenericConstraint(s, typ): + return true + of tyAnd: + for s in t.sons: + if m.checkGenericConstraint(s, typ): + result = true + else: + result = false + break + of tyNot: + if t[0].kind == tyGenericParam: + let tt = PType(idTableGet(m.bindings, t[0])) + if tt != nil: + return not m.checkGenericConstraint(tt, typ) + return not m.checkGenericConstraint(t[0], typ) + of tyBuiltInTypeClass: + return m.checkGenericConstraint(t[0], typ) + of tyTypeDesc: + return m.checkGenericConstraint(t[0], typ[0]) + of tyGenericParam: + if t.len == 0: return true + result = m.checkGenericConstraint(t[0], typ) + else: + return t.kind == typ.kind + proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var IntSet) = template noMatch() = @@ -2421,6 +2472,9 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int elif not (isLValue(c, n, isOutParam(formal.typ))): m.firstMismatch.kind = kVarNeeded noMatch() + if formal.typ.kind == tyGenericParam and formal.typ.len > 0: + if m.checkGenericConstraint(formal.typ, n.typ): + inc(m.genericConstrainMatches, 1) m.state = csMatch # until proven otherwise m.firstMismatch = MismatchInfo() diff --git a/compiler/types.nim b/compiler/types.nim index d2517127a9fd..f5dd8f30cf26 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -42,6 +42,7 @@ type isInferred, # generic proc was matched against a concrete type isInferredConvertible, # same as above, but requiring proc CC conversion isGeneric, + isTypeClass, isFromIntLit, # conversion *from* int literal; proven safe isEqual diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 7ccd3c43f572..6ea5c0ca18b2 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -390,6 +390,11 @@ proc `[]=`*(obj: JsonNode, key: string, val: JsonNode) {.inline.} = assert(obj.kind == JObject) obj.fields[key] = val +proc `%`*(o: enum): JsonNode = + ## Construct a JsonNode that represents the specified enum value as a + ## string. Creates a new `JString JsonNode`. + result = %($o) + proc `%`*[T: object](o: T): JsonNode = ## Construct JsonNode from tuples and objects. result = newJObject() @@ -402,11 +407,6 @@ proc `%`*(o: ref object): JsonNode = else: result = %(o[]) -proc `%`*(o: enum): JsonNode = - ## Construct a JsonNode that represents the specified enum value as a - ## string. Creates a new `JString JsonNode`. - result = %($o) - proc toJsonImpl(x: NimNode): NimNode = case x.kind of nnkBracket: # array diff --git a/lib/pure/strformat.nim b/lib/pure/strformat.nim index 216c1ff11f06..40277968dba2 100644 --- a/lib/pure/strformat.nim +++ b/lib/pure/strformat.nim @@ -567,7 +567,7 @@ proc formatValue*(result: var string; value: string; specifier: string) = setLen(value, runeOffset(value, spec.precision)) result.add alignString(value, spec.minimumWidth, spec.align, spec.fill) -proc formatValue[T: not SomeInteger](result: var string; value: T; specifier: string) = +proc formatValue[T: not SomeNumber](result: var string; value: T; specifier: string) = mixin `$` formatValue(result, $value, specifier) diff --git a/tests/generics/t13549.nim b/tests/generics/t13549.nim new file mode 100644 index 000000000000..d06cf44f13f1 --- /dev/null +++ b/tests/generics/t13549.nim @@ -0,0 +1,10 @@ + +proc foo[T](x: T):int = 1 + +proc foo[T: tuple](x: T):int = 2 + +doAssert foo((1, 2, 3)) == 2 + +type Obj = object +proc foo(x: object):int = 3 +doAssert foo(Obj()) == 3 diff --git a/tests/generics/t18314.nim b/tests/generics/t18314.nim new file mode 100644 index 000000000000..3fd2ffd9790c --- /dev/null +++ b/tests/generics/t18314.nim @@ -0,0 +1,14 @@ +discard """ + disabled: true +""" + +type + A = ref object of RootObj + B = ref object of A + C = ref object of B + +proc foo[T: A](a: T):int = 1 +proc foo[T: B](b: T):int = 2 + +var c = C() +doAssert foo(c) == 2 diff --git a/tests/generics/t4084.nim b/tests/generics/t4084.nim new file mode 100644 index 000000000000..f9fc8dd9542a --- /dev/null +++ b/tests/generics/t4084.nim @@ -0,0 +1,13 @@ +discard """ + disabled: true +""" + +type + Image[T: int32|int64] = object + data: seq[T] + +proc newImage[T](w, h: int): ref Image[T] = + new(result) + result.data = newSeq[T](w * h) + +var i = newImage[string](320, 200) diff --git a/tests/generics/t6407.nim b/tests/generics/t6407.nim new file mode 100644 index 000000000000..ae9378e3a7e0 --- /dev/null +++ b/tests/generics/t6407.nim @@ -0,0 +1,12 @@ +discard """ +action: compile +""" + +proc foo[T6407: SomeFloat](y: T6407):int = 0 + +proc foo[T6407: SomeInteger](y: T6407):int = 1 + +proc boo[T6407](x: T6407):int = + foo[T6407](x) + +doAssert boo(1) == 1 diff --git a/tests/generics/t6407_2.nim b/tests/generics/t6407_2.nim new file mode 100644 index 000000000000..27194028a399 --- /dev/null +++ b/tests/generics/t6407_2.nim @@ -0,0 +1,10 @@ +discard """ +action: compile +""" + +proc sort[T: uint8|char|byte](c: T) = discard +proc sort[T: bool](c: T) = discard + +proc sorted[T](c: T): T = sort[T](c) + +doAssert sorted(true) == false