diff options
-rw-r--r-- | compiler/lineinfos.nim | 1 | ||||
-rw-r--r-- | compiler/sem.nim | 1 | ||||
-rw-r--r-- | compiler/sigmatch.nim | 99 | ||||
-rw-r--r-- | tests/sets/trangeincompatible.nim | 32 | ||||
-rw-r--r-- | tests/sets/twrongenumrange.nim | 5 |
5 files changed, 129 insertions, 9 deletions
diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 5535dc109..94a483299 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -268,6 +268,7 @@ const NotesVerbosity* = computeNotesVerbosity() errXMustBeCompileTime* = "'$1' can only be used in compile-time context" errArgsNeedRunOption* = "arguments can only be given if the '--run' option is selected" + errFloatToString* = "cannot convert '$1' to '$2'" type TFileInfo* = object diff --git a/compiler/sem.nim b/compiler/sem.nim index 72d8dc5b0..2cf93d365 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -497,7 +497,6 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode, const errMissingGenericParamsForTemplate = "'$1' has unspecified generic parameters" - errFloatToString = "cannot convert '$1' to '$2'" proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, flags: TExprFlags = {}; expectedType: PType = nil): PNode = diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 844db3209..6ea2c7bb5 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1561,11 +1561,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, else: result = typeRel(c, f[0], a[0], flags) if result < isGeneric: - if result <= isConvertible: - result = isNone - elif tfIsConstructor notin a.flags: - # set constructors are a bit special... + if tfIsConstructor notin a.flags: + # set['a'..'z'] and set[char] have different representations result = isNone + else: + # but we can convert individual elements of the constructor + result = isConvertible of tyPtr, tyRef: a = reduceToBase(a) if a.kind == f.kind: @@ -2183,6 +2184,81 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate, else: result.add arg +proc convertLiteral(kind: TNodeKind, c: PContext, m: TCandidate; n: PNode, newType: PType): PNode = + # based off changeType but generates implicit conversions instead + template addConsiderNil(s, node) = + let val = node + if val.isNil: return nil + s.add(val) + case n.kind + of nkCurly: + result = copyNode(n) + for i in 0..<n.len: + if n[i].kind == nkRange: + var x = copyNode(n[i]) + x.addConsiderNil convertLiteral(kind, c, m, n[i][0], elemType(newType)) + x.addConsiderNil convertLiteral(kind, c, m, n[i][1], elemType(newType)) + result.add x + else: + result.addConsiderNil convertLiteral(kind, c, m, n[i], elemType(newType)) + result.typ = newType + return + of nkBracket: + result = copyNode(n) + for i in 0..<n.len: + result.addConsiderNil convertLiteral(kind, c, m, n[i], elemType(newType)) + result.typ = newType + return + of nkPar, nkTupleConstr: + let tup = newType.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct}) + if tup.kind == tyTuple: + result = copyNode(n) + if n.len > 0 and n[0].kind == nkExprColonExpr: + # named tuple? + for i in 0..<n.len: + var name = n[i][0] + if name.kind != nkSym: + #globalError(c.config, name.info, "invalid tuple constructor") + return nil + if tup.n != nil: + var f = getSymFromList(tup.n, name.sym.name) + if f == nil: + #globalError(c.config, name.info, "unknown identifier: " & name.sym.name.s) + return nil + result.addConsiderNil convertLiteral(kind, c, m, n[i][1], f.typ) + else: + result.addConsiderNil convertLiteral(kind, c, m, n[i][1], tup[i]) + else: + for i in 0..<n.len: + result.addConsiderNil convertLiteral(kind, c, m, n[i], tup[i]) + result.typ = newType + return + of nkCharLit..nkUInt64Lit: + if n.kind != nkUInt64Lit and not sameTypeOrNil(n.typ, newType) and isOrdinalType(newType): + let value = n.intVal + if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType): + return nil + result = copyNode(n) + result.typ = newType + return + of nkFloatLit..nkFloat64Lit: + if newType.skipTypes(abstractVarRange-{tyTypeDesc}).kind == tyFloat: + if not floatRangeCheck(n.floatVal, newType): + return nil + result = copyNode(n) + result.typ = newType + return + of nkSym: + if n.sym.kind == skEnumField and not sameTypeOrNil(n.sym.typ, newType) and isOrdinalType(newType): + let value = n.sym.position + if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType): + return nil + result = copyNode(n) + result.typ = newType + return + else: discard + return implicitConv(kind, newType, n, m, c) + proc isLValue(c: PContext; n: PNode, isOutParam = false): bool {.inline.} = let aa = isAssignable(nil, n) case aa @@ -2394,7 +2470,20 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, if f.skipTypes({tyRange}).kind in {tyInt, tyUInt}: inc(m.convMatches) inc(m.convMatches) - result = implicitConv(nkHiddenStdConv, f, arg, m, c) + if skipTypes(f, abstractVar-{tyTypeDesc}).kind == tySet: + if tfIsConstructor in a.flags and arg.kind == nkCurly: + # we marked the set as convertible only because the arg is a literal + # in which case we individually convert each element + let t = + if containsGenericType(f): + getInstantiatedType(c, arg, m, f).skipTypes({tySink}) + else: + f.skipTypes({tySink}) + result = convertLiteral(nkHiddenStdConv, c, m, arg, t) + else: + result = nil + else: + result = implicitConv(nkHiddenStdConv, f, arg, m, c) of isIntConv: # I'm too lazy to introduce another ``*matches`` field, so we conflate # ``isIntConv`` and ``isIntLit`` here: diff --git a/tests/sets/trangeincompatible.nim b/tests/sets/trangeincompatible.nim new file mode 100644 index 000000000..554a50235 --- /dev/null +++ b/tests/sets/trangeincompatible.nim @@ -0,0 +1,32 @@ +block: # issue #20142 + let + s1: set['a' .. 'g'] = {'a', 'e'} + s2: set['a' .. 'g'] = {'b', 'c', 'd', 'f'} # this works fine + s3 = {'b', 'c', 'd', 'f'} + + doAssert s1 != s2 + doAssert s1 == {range['a'..'g'] 'a', 'e'} + doAssert s2 == {range['a'..'g'] 'b', 'c', 'd', 'f'} + # literal conversion: + doAssert s1 == {'a', 'e'} + doAssert s2 == {'b', 'c', 'd', 'f'} + doAssert s3 == {'b', 'c', 'd', 'f'} + doAssert not compiles(s1 == s3) + doAssert not compiles(s2 == s3) + # can't convert literal 'z', overload match fails + doAssert not compiles(s1 == {'a', 'z'}) + +block: # issue #18396 + var s1: set[char] = {'a', 'b'} + var s2: set['a'..'z'] = {'a', 'b'} + doAssert s1 == {'a', 'b'} + doAssert s2 == {range['a'..'z'] 'a', 'b'} + doAssert s2 == {'a', 'b'} + doAssert not compiles(s1 == s2) + +block: # issue #16270 + var s1: set[char] = {'a', 'b'} + var s2: set['a'..'z'] = {'a', 'c'} + doAssert not (compiles do: s2 = s2 + s1) + s2 = s2 + {'a', 'b'} + doAssert s2 == {'a', 'b', 'c'} diff --git a/tests/sets/twrongenumrange.nim b/tests/sets/twrongenumrange.nim index fc90c142f..a8d64ac44 100644 --- a/tests/sets/twrongenumrange.nim +++ b/tests/sets/twrongenumrange.nim @@ -45,7 +45,6 @@ block: TNoteKind = range[k1..k2] var notes: set[TNoteKind] notes = {k0} #[tt.Error - ^ cannot convert 'k0' to 'TNoteKind = range 1..2(TMsgKind)]# + ^ type mismatch: got <set[TMsgKind]> but expected 'set[TNoteKind]']# notes = {k0..k3} #[tt.Error - ^ cannot convert 'k0' to 'TNoteKind = range 1..2(TMsgKind)'; tt.Error - ^ cannot convert 'k3' to 'TNoteKind = range 1..2(TMsgKind)']# + ^ type mismatch: got <set[TMsgKind]> but expected 'set[TNoteKind]']# |