fixes #287; bugfix: subrange checking is performed again

This commit is contained in:
Araq
2013-04-12 16:24:58 +02:00
parent f5db2de696
commit 3cb3813eed
14 changed files with 147 additions and 32 deletions

View File

@@ -1331,8 +1331,14 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) =
var a, b, x, y: TLoc
if (e.sons[1].Kind == nkCurly) and fewCmps(e.sons[1]):
# a set constructor but not a constant set:
# do not emit the set, but generate a bunch of comparisons
initLocExpr(p, e.sons[2], a)
# do not emit the set, but generate a bunch of comparisons; and if we do
# so, we skip the unnecessary range check: This is a semantical extension
# that code now relies on. :-/ XXX
let ea = if e.sons[2].kind in {nkChckRange, nkChckRange64}:
e.sons[2].sons[0]
else:
e.sons[2]
initLocExpr(p, ea, a)
initLoc(b, locExpr, e.typ, OnUnknown)
b.r = toRope("(")
var length = sonsLen(e.sons[1])

View File

@@ -27,7 +27,7 @@ proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
proc semProcBody(c: PContext, n: PNode): PNode
proc fitNode(c: PContext, formal: PType, arg: PNode): PNode
proc changeType(n: PNode, newType: PType)
proc changeType(n: PNode, newType: PType, check: bool)
proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode
proc semTypeNode(c: PContext, n: PNode, prev: PType): PType

View File

@@ -354,11 +354,11 @@ proc overloadedCallOpr(c: PContext, n: PNode): PNode =
for i in countup(0, sonsLen(n) - 1): addSon(result, n.sons[i])
result = semExpr(c, result)
proc changeType(n: PNode, newType: PType) =
proc changeType(n: PNode, newType: PType, check: bool) =
case n.kind
of nkCurly, nkBracket:
for i in countup(0, sonsLen(n) - 1):
changeType(n.sons[i], elemType(newType))
changeType(n.sons[i], elemType(newType), check)
of nkPar:
if newType.kind != tyTuple:
InternalError(n.info, "changeType: no tuple type for constructor")
@@ -373,15 +373,21 @@ proc changeType(n: PNode, newType: PType) =
if f == nil:
internalError(m.info, "changeType(): invalid identifier")
return
changeType(n.sons[i].sons[1], f.typ)
changeType(n.sons[i].sons[1], f.typ, check)
else:
for i in countup(0, sonsLen(n) - 1):
var m = n.sons[i]
var a = newNodeIT(nkExprColonExpr, m.info, newType.sons[i])
addSon(a, newSymNode(newType.n.sons[i].sym))
addSon(a, m)
changeType(m, newType.sons[i])
changeType(m, newType.sons[i], check)
n.sons[i] = a
of nkCharLit..nkUInt64Lit:
if check:
let value = n.intVal
if value < firstOrd(newType) or value > lastOrd(newType):
LocalError(n.info, errGenerated, "cannot convert " & $value &
" to " & typeToString(newType))
else: nil
n.typ = newType
@@ -461,7 +467,7 @@ proc fixAbstractType(c: PContext, n: PNode) =
elif skipTypes(it.sons[1].typ, abstractVar).kind in
{tyNil, tyArrayConstr, tyTuple, tySet}:
var s = skipTypes(it.typ, abstractVar)
changeType(it.sons[1], s)
changeType(it.sons[1], s, check=true)
n.sons[i] = it.sons[1]
of nkBracket:
# an implicitely constructed array (passed to an open array):
@@ -560,11 +566,12 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
call.add(n.sons[0])
var allConst = true
for i in 1 .. < n.len:
let a = getConstExpr(c.module, n.sons[i])
if a != nil: call.add(a)
else:
var a = getConstExpr(c.module, n.sons[i])
if a == nil:
allConst = false
call.add(n.sons[i])
a = n.sons[i]
if a.kind == nkHiddenStdConv: a = a.sons[1]
call.add(a)
if allConst:
result = semfold.getConstExpr(c.module, call)
if result.isNil: result = n

View File

@@ -458,21 +458,28 @@ proc getAppType(n: PNode): PNode =
else:
result = newStrNodeT("console", n)
proc foldConv*(n, a: PNode): PNode =
proc rangeCheck(n: PNode, value: biggestInt) =
if value < firstOrd(n.typ) or value > lastOrd(n.typ):
LocalError(n.info, errGenerated, "cannot convert " & $value &
" to " & typeToString(n.typ))
proc foldConv*(n, a: PNode; check = false): PNode =
# XXX range checks?
case skipTypes(n.typ, abstractRange).kind
of tyInt..tyInt64:
case skipTypes(a.typ, abstractRange).kind
of tyFloat..tyFloat64: result = newIntNodeT(system.toInt(getFloat(a)), n)
of tyFloat..tyFloat64:
result = newIntNodeT(system.toInt(getFloat(a)), n)
of tyChar: result = newIntNodeT(getOrdValue(a), n)
else:
result = a
result.typ = n.typ
of tyFloat..tyFloat64:
if check: rangeCheck(n, result.intVal)
of tyFloat..tyFloat64:
case skipTypes(a.typ, abstractRange).kind
of tyInt..tyInt64, tyEnum, tyBool, tyChar:
result = newFloatNodeT(toFloat(int(getOrdValue(a))), n)
else:
else:
result = a
result.typ = n.typ
of tyOpenArray, tyVarargs, tyProc:
@@ -694,8 +701,8 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
result.typ = n.typ
of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkCast:
var a = getConstExpr(m, n.sons[1])
if a == nil: return
result = foldConv(n, a)
if a == nil: return
result = foldConv(n, a, check=n.kind == nkHiddenStdConv)
of nkBracketExpr: result = foldArrayAccess(m, n)
of nkDotExpr: result = foldFieldAccess(m, n)
else:

View File

@@ -166,10 +166,10 @@ proc semCase(c: PContext, n: PNode): PNode =
proc fitRemoveHiddenConv(c: PContext, typ: Ptype, n: PNode): PNode =
result = fitNode(c, typ, n)
if result.kind in {nkHiddenStdConv, nkHiddenSubConv}:
changeType(result.sons[1], typ)
changeType(result.sons[1], typ, check=true)
result = result.sons[1]
elif not sameType(result.typ, typ):
changeType(result, typ)
elif not sameType(result.typ, typ):
changeType(result, typ, check=false)
proc findShadowedVar(c: PContext, v: PSym): PSym =
for i in countdown(c.tab.tos - 2, ModuleTablePos+1):

View File

@@ -362,6 +362,22 @@ proc matchTypeClass(c: var TCandidate, f, a: PType): TTypeRelation =
result = if matchTypeClass(c.bindings, f, a): isGeneric
else: isNone
proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
let
a0 = firstOrd(a)
a1 = lastOrd(a)
f0 = firstOrd(f)
f1 = lastOrd(f)
if a0 == f0 and a1 == f1:
result = isEqual
elif a0 >= f0 and a1 <= f1:
result = isConvertible
elif a0 <= f1 and f0 <= a1:
# X..Y and C..D overlap iff (X <= D and C <= Y)
result = isConvertible
else:
result = isNone
proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
# is a subtype of f?
result = isNone
@@ -386,6 +402,8 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
result = typeRel(c, base(f), base(a))
# bugfix: accept integer conversions here
#if result < isGeneric: result = isNone
if result notin {isNone, isGeneric}:
result = typeRangeRel(f, a)
elif skipTypes(f, {tyRange}).kind == a.kind:
result = isIntConv
elif isConvertibleToRange(skipTypes(f, {tyRange}), a):
@@ -704,7 +722,8 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType,
result = implicitConv(nkHiddenSubConv, f, copyTree(arg), m, c)
of isSubrange:
inc(m.subtypeMatches)
result = copyTree(arg)
#result = copyTree(arg)
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
of isGeneric:
inc(m.genericMatches)
if m.calleeSym != nil and m.calleeSym.kind in {skMacro, skTemplate}:

View File

@@ -323,10 +323,10 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
of tyInt..tyInt64, tyEnum, tyChar, tyBool, tyUInt8..tyUInt32:
# we don't include uint and uint64 here as these are no ordinal types ;-)
if not isOrdinalType(source):
# XXX int64 -> float conversion?
# float -> int conversions. ugh.
result = transformSons(c, n)
elif firstOrd(dest) <= firstOrd(source) and
lastOrd(source) <= lastOrd(dest):
elif firstOrd(n.typ) <= firstOrd(n.sons[1].typ) and
lastOrd(n.sons[1].typ) <= lastOrd(n.typ):
# BUGFIX: simply leave n as it is; we need a nkConv node,
# but no range check:
result = transformSons(c, n)
@@ -334,13 +334,14 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
# generate a range check:
if dest.kind == tyInt64 or source.kind == tyInt64:
result = newTransNode(nkChckRange64, n, 3)
else:
else:
result = newTransNode(nkChckRange, n, 3)
dest = skipTypes(n.typ, abstractVar)
result[0] = transform(c, n.sons[1])
result[1] = newIntTypeNode(nkIntLit, firstOrd(dest), source).PTransNode
result[2] = newIntTypeNode(nkIntLit, lastOrd(dest), source).PTransNode
of tyFloat..tyFloat128:
of tyFloat..tyFloat128:
# XXX int64 -> float conversion?
if skipTypes(n.typ, abstractVar).kind == tyRange:
result = newTransNode(nkChckRangeF, n, 3)
dest = skipTypes(n.typ, abstractVar)

View File

@@ -485,8 +485,8 @@ proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
add(result, ']')
of tyPtr, tyRef, tyVar, tyMutable, tyConst:
result = typeToStr[t.kind] & typeToString(t.sons[0])
of tyRange:
result = "range " & rangeToStr(t.n)
of tyRange:
result = "range " & rangeToStr(t.n) & "(" & typeToString(t.sons[0]) & ")"
of tyProc:
result = if tfIterator in t.flags: "iterator (" else: "proc ("
for i in countup(1, sonsLen(t) - 1):

View File

@@ -103,5 +103,5 @@ On UNIX
* ``bin/nimrod c koch``
* ``./koch boot -d:release``
Installation on UNIX can then be done with ``koch install``.
Installation on UNIX can then be done with ``koch install [dir]``.

View File

@@ -0,0 +1,19 @@
discard """
output: "right proc called"
"""
type
TMatrixNM*[M, N, T] = object
aij*: array[M, array[N, T]]
TMatrix2x2*[T] = TMatrixNM[range[0..1], range[0..1], T]
TMatrix3x3*[T] = TMatrixNM[range[0..2], range[0..2], T]
proc test*[T] (matrix: TMatrix2x2[T]) =
echo "wrong proc called"
proc test*[T] (matrix: TMatrix3x3[T]) =
echo "right proc called"
var matrix: TMatrix3x3[float]
matrix.test

View File

@@ -0,0 +1,21 @@
discard """
line: 20
errormsg: "cannot convert 60 to TRange"
"""
type
TRange = range[0..40]
proc p(r: TRange) =
nil
var
r: TRange
y = 50
r = y
p y
const
myConst: TRange = 60

19
tests/run/tsubrange.nim Normal file
View File

@@ -0,0 +1,19 @@
discard """
file: "tsubrange.nim"
outputsub: "value 50 out of range [EOutOfRange]"
exitcode: "1"
"""
type
TRange = range[0..40]
proc p(r: TRange) =
nil
var
r: TRange
y = 50
r = y
#p y

17
tests/run/tsubrange2.nim Normal file
View File

@@ -0,0 +1,17 @@
discard """
file: "tsubrange2.nim"
outputsub: "value 50 out of range [EOutOfRange]"
exitcode: "1"
"""
type
TRange = range[0..40]
proc p(r: TRange) =
nil
var
r: TRange
y = 50
p y

View File

@@ -13,7 +13,6 @@ Bugs
====
- docgen: sometimes effects are listed twice
- even the easiest range checking is not performed anymore
- 'result' is not properly cleaned for NRVO
- instantiated generics are listed in error messages
- sneaking with qualifiedLookup() is really broken!
@@ -23,6 +22,7 @@ Bugs
- osproc execProcesses can deadlock if all processes fail (as experienced
in c++ mode)
- bootstrapping does not work in C++ mode
- result = result shr 8 for the "system()" wrapper
version 0.9.4
@@ -54,7 +54,6 @@ version 0.9.X
- improve the compiler as a service
- better support for macros that rewrite procs
- macros need access to types and symbols (partially implemented)
- result = result shr 8 for the "system()" wrapper
- rethink the syntax/grammar:
* parser is not strict enough with newlines
* change comment handling in the AST