Skip to content

Commit

Permalink
add literal conversion behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
metagn committed Sep 25, 2024
1 parent 2bd4016 commit 1625ba3
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 11 deletions.
9 changes: 5 additions & 4 deletions compiler/lineinfos.nim
Original file line number Diff line number Diff line change
Expand Up @@ -257,19 +257,20 @@ type
proc computeNotesVerbosity(): array[0..3, TNoteKinds] =
result = default(array[0..3, TNoteKinds])
{.push warning[EnumConv]: off.}
result[3] = {low(TNoteKind)..high(TNoteKind)} - {TNoteKind warnObservableStores, warnResultUsed, warnAnyEnumConv, warnBareExcept, warnStdPrefix}
result[3] = {low(TNoteKind)..high(TNoteKind)} - {warnObservableStores, warnResultUsed, warnAnyEnumConv, warnBareExcept, warnStdPrefix}
{.pop.}
result[2] = result[3] - {TNoteKind hintStackTrace, hintExtendedContext, hintDeclaredLoc, hintProcessingStmt}
result[1] = result[2] - {TNoteKind warnProveField, warnProveIndex,
result[2] = result[3] - {hintStackTrace, hintExtendedContext, hintDeclaredLoc, hintProcessingStmt}
result[1] = result[2] - {warnProveField, warnProveIndex,
warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
hintSource, hintGlobalVar, hintGCStats, hintMsgOrigin, hintPerformance}
result[0] = result[1] - {TNoteKind hintSuccessX, hintSuccess, hintConf,
result[0] = result[1] - {hintSuccessX, hintSuccess, hintConf,
hintProcessing, hintPattern, hintExecuting, hintLinking, hintCC}

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
Expand Down
1 change: 0 additions & 1 deletion compiler/sem.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
98 changes: 93 additions & 5 deletions compiler/sigmatch.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1561,12 +1561,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
else:
result = typeRel(c, f[0], a[0], flags)
if result < isGeneric:
if result <= isSubrange:
if tfIsConstructor notin a.flags:
# set['a'..'z'] and set[char] have different representations
result = isNone
elif tfIsConstructor notin a.flags:
# set constructors are a bit special...
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:
Expand Down Expand Up @@ -2184,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
Expand Down Expand Up @@ -2395,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:
Expand Down
9 changes: 8 additions & 1 deletion tests/sets/trangeincompatible.nim
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,28 @@ block: # issue #20142
s3 = {'b', 'c', 'd', 'f'}

doAssert s1 != s2
# compiler can't automatically convert this yet:
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'}

0 comments on commit 1625ba3

Please sign in to comment.