> On Jun 2, 2016, at 2:36 PM, Vladimir.S <[email protected]> wrote: > Well, I understand that it seems like there is some problem with UIntN() > initialization, but can't find any simple code that will demonstrate this.. > > All below works as expected: > > var x1: Int32 = 0 > var x2 = Int32(0) > > print(x1.dynamicType, x2.dynamicType) // Int32 Int32 > > // integer overflows when converted from 'Int' to 'UInt16' > //var x = UInt16(100_000) > //var x = UInt16(-10) > > // negative integer cannot be converted to unsigned type 'UInt64' > // var x = UInt64(-1) > > So, what code will produce some unexpected behavior / error at runtime?
Like I said, the standard library and compiler conspire to make sure that easy cases like this are caught at compile time, but that would not help non-standard types that conform to IntegerLiteralConvertible. Also, even for standard types, the syntax only works statically if the literal fits in the range of Int, which may not be a superset of the desired type. For example, UInt64(0x10000000000) would not work on a 32-bit platform. It is diagnosed statically, however. John. > > On 03.06.2016 0:25, John McCall wrote: >>> On Jun 2, 2016, at 1:56 PM, Vladimir.S <[email protected]> wrote: >>>> Often >>>> this leads to static ambiguities or, worse, causes the literal to be built >>>> using a default type (such as Int); this may have semantically very >>>> different results which are only caught at runtime. >>> >>> Seems like I'm very slow today.. Could you present a couple of examples >>> where such initialization(like UInt16(7)) can produce some unexpected >>> behavior / error at runtime? >> >> UIntN has unlabeled initializers taking all of the standard integer types, >> including itself. The literal type will therefore get defaulted to Int. >> The legal range of values for Int may not be a superset of the legal range >> of values for UIntN. If the literal is in the legal range for an Int but >> not for the target type, this might trap at runtime. Now, for a built-in >> integer type like UInt16, we will recognize that the coercion always traps >> and emit an error at compile-time, but this generally won't apply to other >> types. >> >> John. >> >>> >>> On 02.06.2016 19:08, John McCall via swift-evolution wrote: >>>> The official way to build a literal of a specific type is to write the >>>> literal in an explicitly-typed context, like so: >>>> let x: UInt16 = 7 >>>> or >>>> let x = 7 as UInt16 >>>> >>>> Nonetheless, programmers often try the following: >>>> UInt16(7) >>>> >>>> Unfortunately, this does /not/ attempt to construct the value using the >>>> appropriate literal protocol; it instead performs overload resolution using >>>> the standard rules, i.e. considering only single-argument unlabelled >>>> initializers of a type which conforms to IntegerLiteralConvertible. Often >>>> this leads to static ambiguities or, worse, causes the literal to be built >>>> using a default type (such as Int); this may have semantically very >>>> different results which are only caught at runtime. >>>> >>>> In my opinion, using this initializer-call syntax to build an >>>> explicitly-typed literal is an obvious and natural choice with several >>>> advantages over the "as" syntax. However, even if you disagree, it's clear >>>> that programmers are going to continue to independently try to use it, so >>>> it's really unfortunate for it to be subtly wrong. >>>> >>>> Therefore, I propose that we adopt the following typing rule: >>>> >>>> Given a function call expression of the form A(B) (that is, an >>>> /expr-call/ with a single, unlabelled argument) where B is >>>> an /expr-literal/ or /expr-collection/, if A has type T.Type for some type >>>> T and there is a declared conformance of T to an appropriate literal >>>> protocol for B, then the expression is always resolves as a literal >>>> construction of type T (as if the expression were written "B as A") rather >>>> than as a general initializer call. >>>> >>>> Formally, this would be a special form of the argument conversion >>>> constraint, since the type of the expression A may not be immediately >>>> known. >>>> >>>> Note that, as specified, it is possible to suppress this typing rule by >>>> wrapping the literal in parentheses. This might seem distasteful; it would >>>> be easy enough to allow the form of B to include extra parentheses. It's >>>> potentially useful to have a way to suppress this rule and get a normal >>>> construction, but there are several other ways of getting that effect, such >>>> as explicitly typing the literal argument (e.g. writing "A(Int(B))"). >>>> >>>> A conditional conformance counts as a declared conformance even if the >>>> generic arguments are known to not satisfy the conditional conformance. >>>> This permits the applicability of the rule to be decided without having to >>>> first decide the type arguments, which greatly simplifies the type-checking >>>> problem (and may be necessary for soundness; I didn't explore this in >>>> depth, but it certainly feels like a very nasty sort of dependence). We >>>> could potentially weaken this for cases where A is a direct type reference >>>> with bound parameters, e.g. Foo<Int>([]) or the same with a typealias, but >>>> I think there's some benefit from having a simpler specification, both for >>>> the implementation and for the explicability of the model. >>>> >>>> John. >>>> >>>> >>>> _______________________________________________ >>>> swift-evolution mailing list >>>> [email protected] >>>> https://lists.swift.org/mailman/listinfo/swift-evolution >>>> >> >> _______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
