This commit is contained in:
ringabout
2026-01-22 15:11:44 +08:00
parent 98369bb1f8
commit da98702bbd
2 changed files with 36 additions and 37 deletions

View File

@@ -148,6 +148,37 @@ proc isLocalSym(a: PEffects, s: PSym): bool =
s.typ != nil and (s.kind in {skLet, skVar, skResult} or (s.kind == skParam and isOutParam(s.typ))) and
sfGlobal notin s.flags and s.owner == a.owner
proc isRangeSupertype(conf: ConfigRef; wider, narrower: PType): bool =
## Check if `wider` type fully contains `narrower` type
## Returns true if narrower fits entirely within wider (safe conversion)
if wider.isOrdinalType:
let wideFirst = firstOrd(conf, wider)
let wideLast = lastOrd(conf, wider)
let narrowFirst = firstOrd(conf, narrower)
let narrowLast = lastOrd(conf, narrower)
result = narrowFirst >= wideFirst and narrowLast <= wideLast
elif not narrower.isOrdinalType:
let wideFirst = firstFloat(wider)
let wideLast = lastFloat(wider)
let narrowFirst = firstFloat(narrower)
let narrowLast = lastFloat(narrower)
result = narrowFirst >= wideFirst and narrowLast <= wideLast
else:
# int -> float ranges; warn
result = false
proc shouldWarnRangeConversion(conf: ConfigRef; formalType, argType: PType): bool =
## Determine if an implicit range conversion should warn
## We warn on conversions that are likely to cause panics
let f = formalType.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
let a = argType.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
if f.kind == tyRange:
# Only warn if formal range doesn't fully contain argument range
# Check if the ranges don't perfectly overlap
result = not isRangeSupertype(conf, f, a)
else:
result = false
proc lockLocations(a: PEffects; pragma: PNode) =
if pragma.kind != nkExprColonExpr:
localError(a.config, pragma.info, "locks pragma without argument")
@@ -1504,6 +1535,11 @@ proc track(tracked: PEffects, n: PNode) =
message(tracked.config, n.info, warnPtrToCstringConv,
$n[1].typ)
# Check for implicit range conversions
if n.kind == nkHiddenStdConv and
shouldWarnRangeConversion(tracked.config, n.typ, n[1].typ):
message(tracked.config, n.info, warnImplicitRangeConversion,
typeToString(n[1].typ) & " -> " & typeToString(n.typ))
let t = n.typ.skipTypes(abstractInst)
if t.kind == tyEnum:

View File

@@ -615,36 +615,7 @@ proc isGenericObjectOf(f, a: PType): bool =
# use sym equality to check if the `tyGenericBody` types are equal
result = aRoot != nil and f.sym == aRoot.sym
proc isRangeSupertype(conf: ConfigRef; wider, narrower: PType): bool =
## Check if `wider` type fully contains `narrower` type
## Returns true if narrower fits entirely within wider (safe conversion)
if wider.isOrdinalType:
let wideFirst = firstOrd(conf, wider)
let wideLast = lastOrd(conf, wider)
let narrowFirst = firstOrd(conf, narrower)
let narrowLast = lastOrd(conf, narrower)
result = narrowFirst >= wideFirst and narrowLast <= wideLast
elif not narrower.isOrdinalType:
let wideFirst = firstFloat(wider)
let wideLast = lastFloat(wider)
let narrowFirst = firstFloat(narrower)
let narrowLast = lastFloat(narrower)
result = narrowFirst >= wideFirst and narrowLast <= wideLast
else:
# int -> float ranges; ignore for now
result = true
proc shouldWarnRangeConversion(conf: ConfigRef; formalType, argType: PType): bool =
## Determine if an implicit range conversion should warn
## We warn on conversions that are likely to cause panics
let f = formalType.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
let a = argType.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
if f.kind == tyRange:
# Only warn if formal range doesn't fully contain argument range
# Check if the ranges don't perfectly overlap
result = not isRangeSupertype(conf, f, a)
else:
result = false
proc isObjectSubtype(c: var TCandidate; a, f, fGenericOrigin: PType): int =
var t = a
@@ -2518,10 +2489,6 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
case r
of isConvertible:
# Check for problematic implicit range conversions
if shouldWarnRangeConversion(c.config, f, a):
message(c.config, arg.info, warnImplicitRangeConversion,
typeToString(a) & " -> " & typeToString(f))
if f.skipTypes({tyRange}).kind in {tyInt, tyUInt}:
inc(m.convMatches)
inc(m.convMatches)
@@ -2542,10 +2509,6 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
of isIntConv:
# I'm too lazy to introduce another ``*matches`` field, so we conflate
# ``isIntConv`` and ``isIntLit`` here:
# Check for problematic implicit range conversions
if shouldWarnRangeConversion(c.config, f, a):
message(c.config, arg.info, warnImplicitRangeConversion,
typeToString(a) & " -> " & typeToString(f))
if f.skipTypes({tyRange}).kind notin {tyInt, tyUInt}:
inc(m.intConvMatches)
inc(m.intConvMatches)