diff --git a/compiler/layeredtable.nim b/compiler/layeredtable.nim index 565fb9546411a..52ae5a09235ed 100644 --- a/compiler/layeredtable.nim +++ b/compiler/layeredtable.nim @@ -80,3 +80,23 @@ proc put(typeMap: var LayeredIdTable, key: ItemId, value: PType) {.inline.} = template put*(typeMap: var LayeredIdTable, key, value: PType) = ## binds `key` to `value` only in current layer put(typeMap, key.itemId, value) + +proc putRecursive(typeMap: ref LayeredIdTableObj, key: ItemId, value: PType) = + var tm = typeMap + while tm != nil: + tm.topLayer[key] = value + tm = tm.nextLayer + +template putRecursive*(typeMap: ref LayeredIdTableObj, key, value: PType) = + ## binds `key` to `value` in all previous layers + putRecursive(typeMap, key.itemId, value) + +when not useRef: + proc putRecursive(typeMap: var LayeredIdTableObj, key: ItemId, value: PType) {.inline.} = + put(typeMap, key, value) + if typeMap.nextLayer != nil: + putRecursive(typeMap.nextLayer, key, value) + + template putRecursive*(typeMap: var LayeredIdTableObj, key, value: PType) = + ## binds `key` to `value` in all previous layers + putRecursive(typeMap, key.itemId, value) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index e50b50331ca83..bef1d9eff8d87 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1280,12 +1280,17 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, if a.isResolvedUserTypeClass: return typeRel(c, f, a.skipModifier, flags) - template bindingRet(res) = + template bindingRet(res, bound) = if doBind: - let bound = aOrig.skipTypes({tyRange}).skipIntLit(c.c.idgen) put(c, f, bound) return res + template defaultBinding(): PType = + aOrig#[.skipTypes({tyRange})]#.skipIntLit(c.c.idgen) + + template bindingRet(res) = + bindingRet(res, defaultBinding()) + template considerPreviousT(body: untyped) = var prev = lookup(c.bindings, f) if prev == nil: body @@ -1831,18 +1836,42 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, result = isNone let oldInheritancePenalty = c.inheritancePenalty var minInheritance = maxInheritancePenalty + var bestMatch: PType = nil + # the best match in the branches so far, preferring earlier branches + # this is to resolve ambiguity in cases like matching an int literal + # against `int8 | int16`, picking the earlier `int8` + # this keeps compatibility with legacy behavior for branch in f.kids: c.inheritancePenalty = -1 let x = typeRel(c, branch, aOrig, flags) if x >= result: if c.inheritancePenalty > -1: - minInheritance = min(minInheritance, c.inheritancePenalty) + if c.inheritancePenalty < minInheritance: + minInheritance = c.inheritancePenalty + if x > result: + # has to be strictly better so we prefer earlier matches + # also true for inheritance penalty in this case + bestMatch = branch + elif x > result: + # has to be strictly better so we prefer earlier matches + bestMatch = branch result = x if result >= isIntConv: if minInheritance < maxInheritancePenalty: c.inheritancePenalty = oldInheritancePenalty + minInheritance + var bound: PType = nil + if (result in {isConvertible, isIntConv, isSubrange, isFromIntLit} or + (result == isSubtype and a.isEmptyContainer)) and bestMatch != nil: + # match needs a conversion, bind to the constraint so the + # conversion can be generated + # supertypes act like typeclasses, only consider as concrete for + # empty collections + bound = bestMatch + else: + # default, bind to matched type + bound = defaultBinding() if result > isGeneric: result = isGeneric - bindingRet result + bindingRet result, bound else: result = isNone of tyNot: @@ -1924,6 +1953,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, let doBindGP = doBind or trBindGenericParam in flags var x = lookup(c.bindings, f) if x == nil: + var bound: PType = nil if c.callee.kind == tyGenericBody and not c.typedescMatched: # XXX: The fact that generic types currently use tyGenericParam for # their parameters is really a misnomer. tyGenericParam means "match @@ -1957,11 +1987,35 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, else: # check if 'T' has a constraint as in 'proc p[T: Constraint](x: T)' if f.len > 0 and f[0].kind != tyNone: - result = typeRel(c, f[0], a, flags + {trDontBind, trBindGenericParam}) + # constraints need a new type binding context layer, + # so that matches to typeclasses in the constraint don't + # bind to that typeclass for the entire candidate + c.bindings = newTypeMapLayer(c.bindings) + # generic params in the constraint are an exception, + # so that we can infer things like `T: U` + result = typeRel(c, f[0], a, flags + {trBindGenericParam}) + if f[0].skipTypes({tyAlias}).kind == tyOr: + # `or` types have special binding rules, they bind to + # one of their branches if the match needs a conversion + # we reuse this binding for the generic parameter + bound = lookup(c.bindings, f[0]) + if bound == nil: bound = a + elif result in {isConvertible, isIntConv, isSubrange, isFromIntLit} or + (result == isSubtype and a.isEmptyContainer): + # match needs a conversion, bind to the constraint so the + # conversion can be generated + # supertypes act like typeclasses, only consider as concrete for + # empty collections + bound = f[0] + else: + # default, bind to matched type + bound = a + setToPreviousLayer(c.bindings) if doBindGP and result notin {isNone, isGeneric}: - let concrete = concreteType(c, a, f) + let concrete = concreteType(c, bound, f) if concrete == nil: return isNone - put(c, f, concrete) + # generic params have to be bound up to the root binding context + putRecursive(c.bindings, f, concrete) if result in {isEqual, isSubtype}: result = isGeneric elif a.kind == tyTypeDesc: @@ -1973,7 +2027,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, result = isGeneric if result == isGeneric: - var concrete = a + if bound == nil: bound = a if tfWildcard in a.flags: a.sym.transitionGenericParamToType() a.flags.excl tfWildcard @@ -1983,11 +2037,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # reason about and maintain. Refactoring typeRel to not be responsible for setting, or # at least validating, bindings can have multiple benefits. This is debatable. I'm not 100% sure. # A design that allows a proper complexity analysis of types like `tyOr` would be ideal. - concrete = concreteType(c, a, f) - if concrete == nil: + bound = concreteType(c, bound, f) + if bound == nil: return isNone if doBindGP: - put(c, f, concrete) + # generic params have to be bound up to the root binding context + putRecursive(c.bindings, f, bound) elif result > isGeneric: result = isGeneric elif a.kind == tyEmpty: diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 78ebb1c88aa2f..3a3255174cd93 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -978,7 +978,7 @@ proc setLastModificationTime*(file: string, t: times.Time) {.noWeirdTarget.} = ## an error. when defined(posix): let unixt = posix.Time(t.toUnix) - let micro = convert(Nanoseconds, Microseconds, t.nanosecond) + let micro = convert(Nanoseconds, Microseconds, t.nanosecond).int32 var timevals = [Timeval(tv_sec: unixt, tv_usec: micro), Timeval(tv_sec: unixt, tv_usec: micro)] # [last access, last modification] if utimes(file, timevals.addr) != 0: raiseOSError(osLastError(), file) diff --git a/tests/overload/torconv.nim b/tests/overload/torconv.nim new file mode 100644 index 0000000000000..2ee4c63294637 --- /dev/null +++ b/tests/overload/torconv.nim @@ -0,0 +1,64 @@ +block: + proc foo(x: int16 | int32): string = $typeof(x) + proc bar[T: int16 | int32](x: T): string = $typeof(x) + + doAssert foo(123) == "int16" + doAssert bar(123) == "int16" + +block: # issue #4858 + type + SomeType = object + field1: uint + + proc namedProc(an: var SomeType, b: SomeUnsignedInt) = discard + + proc `+=`(an: var SomeType, b: SomeUnsignedInt) = + namedProc(an, b) # <---- error here + + var t = SomeType() + namedProc(t, 0) + t += 0 + +block: # issue #10027 + type Uint24 = range[0'u32 .. 0xFFFFFF'u32] + + proc a(v: SomeInteger|Uint24): string = $type(v) + + doAssert a(42) == "int" + doAssert a(42.Uint24) == $Uint24 + +block: # issue #12552 + let x = 1'i8 + proc foo(n : int): string = $typeof(n) + proc bar[T : int](n : T): string = $ typeof(n) + doAssert foo(x) == "int" + doAssert bar(x) == "int" + +block: # issue #15721 + proc fn(a = 4, b: seq[string] or tuple[] = ()) = + discard # eg: when b is tuple[]: ... + fn(1) + fn(1, @[""]) + var a: seq[string] = @[] + fn(1, a) + fn(1, seq[string](@[])) + fn(1, @[]) # BUG: error: conflicting types for 'fn__d58I39cH9a6bcpi3QDPJ5dBA' + +block: # issue #15721, set + proc fn(a = 4, b: set[uint8] or tuple[] = ()) = + discard # eg: when b is tuple[]: ... + fn(1) + fn(1, {1'u8}) + var a: set[uint8] = {} + fn(1, a) + fn(1, set[uint8]({})) + fn(1, {}) # BUG: internal error: invalid kind for lastOrd(tyEmpty) + +block: # issue #21331 + let a : int8 | uint8 = 3 + doAssert sizeof(a)==sizeof(int8) # this fails + +block: + let x: range[0..5] = 1 + proc foo[T: SomeInteger](x: T): string = $typeof(x) + discard foo(x)