mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
The standard library has two versions of the `abs(_:)` function: ``` func abs<T : SignedNumeric>(_ x: T) -> T where T.Magnitude == T func abs<T : SignedNumeric & Comparable>(_ x: T) -> T ``` The first is more specialized than the second because `T.Magnitude` is known to conform to `Comparable`. Indeed, it’s a more specialized implementation that returns `magnitude`. However, this overload behaves oddly: in the expression `abs(-8)`, the type checker will pick the first overload because it is more specialized. That’s a general guiding principle for overloading: pick the most specialized overload that works. However, to select that overload, it needs to pick a type for the literal “8” for which that overload works, and it chooses `Double`. The “obvious” answer, `Int`, doesn’t work because `Int.Magnitude == UInt`. There is a conflict between the two rules, here: we prefer more-specialized overloads (but we’ll fall back to less-specialized if those don’t work) and we prefer to use `Int` for integer literals (but we’ll fall back to `Double` if it doesn’t work). We have a few options from a type-checker perspective: 1. Consider the more-specialized-function rule to be more important 2. Consider the integer-literals-prefer-`Int` rule to be more important 3. Call the result ambiguous and make the user annotate it The type checker currently does #1, although at some point in the past it did #2. Moving forward, #1 is a better choice because it prunes the number of overloads that need to be considered: if the more-specialized overload succeeds its type-check, the others need not be considered. It’s also easier to reason about than the literal-scoring approach, because there can be a direct definition for “more specialized than” that can be reasoned about. I think we should dodge the issue by removing the more-specialized version of `abs(_:)`. Its use of `magnitude` seems unlikely to provide a significant performance benefit, and the presence of overloading either forces us to consider both overloads always (which is bad for type checker performance) or accept the regression that `abs(-8)` is `Double`. Better to eliminate the overloading and, if needed in the future, find a better way to introduce the more-specialized implementation without it being a separate signature. Fixes rdar://problem/42345366.
110 lines
4.4 KiB
Swift
110 lines
4.4 KiB
Swift
import ImportsImportsFoo
|
|
import FooHelper.FooHelperExplicit
|
|
func test() {
|
|
let x = 1
|
|
#^A^#
|
|
}
|
|
|
|
// XFAIL: broken_std_regex
|
|
// REQUIRES: objc_interop
|
|
// RUN: %complete-test -hide-none -group=none -tok=A %s -raw -- -I %S/Inputs -F %S/../Inputs/libIDE-mock-sdk > %t
|
|
// RUN: %FileCheck %s < %t
|
|
|
|
// Swift == 1
|
|
// CHECK-LABEL: key.name: "abs(:)",
|
|
// CHECK-NEXT: key.sourcetext: "abs(<#T##x: Comparable & SignedNumeric##Comparable & SignedNumeric#>)",
|
|
// CHECK-NEXT: key.description: "abs(x: Comparable & SignedNumeric)",
|
|
// CHECK-NEXT: key.typename: "Comparable & SignedNumeric",
|
|
// CHECK-NEXT: key.doc.brief: "Returns the absolute value of the given number.",
|
|
// CHECK-NEXT: key.context: source.codecompletion.context.othermodule,
|
|
// CHECK-NEXT: key.moduleimportdepth: 1,
|
|
// CHECK-NEXT: key.num_bytes_to_erase: 0,
|
|
// CHECK-NOT: key.modulename
|
|
// CHECK: key.modulename: "Swift"
|
|
// CHECK-NEXT: },
|
|
|
|
// FooHelper.FooHelperExplicit == 1
|
|
// CHECK-LABEL: key.name: "fooHelperExplicitFrameworkFunc1(:)",
|
|
// CHECK-NEXT: key.sourcetext: "fooHelperExplicitFrameworkFunc1(<#T##a: Int32##Int32#>)",
|
|
// CHECK-NEXT: key.description: "fooHelperExplicitFrameworkFunc1(a: Int32)",
|
|
// CHECK-NEXT: key.typename: "Int32",
|
|
// CHECK-NEXT: key.context: source.codecompletion.context.othermodule,
|
|
// CHECK-NEXT: key.moduleimportdepth: 1,
|
|
// CHECK-NEXT: key.num_bytes_to_erase: 0,
|
|
// CHECK-NOT: key.modulename
|
|
// CHECK: key.modulename: "FooHelper.FooHelperExplicit"
|
|
// CHECK-NEXT: },
|
|
|
|
// ImportsImportsFoo == 1
|
|
// CHECK-LABEL: key.name: "importsImportsFoo()",
|
|
// CHECK-NEXT: key.sourcetext: "importsImportsFoo()",
|
|
// CHECK-NEXT: key.description: "importsImportsFoo()",
|
|
// CHECK-NEXT: key.typename: "Void",
|
|
// CHECK-NEXT: key.context: source.codecompletion.context.othermodule,
|
|
// CHECK-NEXT: key.moduleimportdepth: 1,
|
|
// CHECK-NEXT: key.num_bytes_to_erase: 0,
|
|
// CHECK-NOT: key.modulename
|
|
// CHECK: key.modulename: "ImportsImportsFoo"
|
|
// CHECK-NEXT: },
|
|
|
|
// Bar == 2
|
|
// CHECK-LABEL: key.name: "BarForwardDeclaredClass",
|
|
// CHECK-NEXT: key.sourcetext: "BarForwardDeclaredClass",
|
|
// CHECK-NEXT: key.description: "BarForwardDeclaredClass",
|
|
// CHECK-NEXT: key.typename: "BarForwardDeclaredClass",
|
|
// CHECK-NEXT: key.context: source.codecompletion.context.othermodule,
|
|
// CHECK-NEXT: key.moduleimportdepth: 2,
|
|
// CHECK-NEXT: key.num_bytes_to_erase: 0,
|
|
// CHECK-NOT: key.modulename
|
|
// CHECK: key.modulename: "Bar"
|
|
// CHECK-NEXT: },
|
|
|
|
// ImportsFoo == 2
|
|
// CHECK-LABEL: key.name: "importsFoo()",
|
|
// CHECK-NEXT: key.sourcetext: "importsFoo()",
|
|
// CHECK-NEXT: key.description: "importsFoo()",
|
|
// CHECK-NEXT: key.typename: "Void",
|
|
// CHECK-NEXT: key.context: source.codecompletion.context.othermodule,
|
|
// CHECK-NEXT: key.moduleimportdepth: 2,
|
|
// CHECK-NEXT: key.num_bytes_to_erase: 0,
|
|
// CHECK-NOT: key.modulename
|
|
// CHECK: key.modulename: "ImportsFoo"
|
|
// CHECK-NEXT: },
|
|
|
|
// Foo == FooSub == 3
|
|
// CHECK-LABEL: key.name: "FooClassBase",
|
|
// CHECK-NEXT: key.sourcetext: "FooClassBase",
|
|
// CHECK-NEXT: key.description: "FooClassBase",
|
|
// CHECK-NEXT: key.typename: "FooClassBase",
|
|
// CHECK-NEXT: key.context: source.codecompletion.context.othermodule,
|
|
// CHECK-NEXT: key.moduleimportdepth: 3,
|
|
// CHECK-NEXT: key.num_bytes_to_erase: 0,
|
|
// CHECK-NOT: key.modulename
|
|
// CHECK: key.modulename: "Foo"
|
|
// CHECK-NEXT: },
|
|
|
|
// CHECK-LABEL: key.name: "FooSubEnum1",
|
|
// CHECK-NEXT: key.sourcetext: "FooSubEnum1",
|
|
// CHECK-NEXT: key.description: "FooSubEnum1",
|
|
// CHECK-NEXT: key.typename: "FooSubEnum1",
|
|
// CHECK-NEXT: key.context: source.codecompletion.context.othermodule,
|
|
// CHECK-NEXT: key.moduleimportdepth: 3,
|
|
// CHECK-NEXT: key.num_bytes_to_erase: 0,
|
|
// CHECK-NOT: key.modulename
|
|
// CHECK: key.modulename: "Foo.FooSub"
|
|
// CHECK-NEXT: },
|
|
|
|
// FooHelper == 4
|
|
// FIXME: rdar://problem/20230030
|
|
// We're picking up the implicit import of FooHelper used to attach FooHelperExplicit to.
|
|
// xCHECK-LABEL: key.name: "FooHelperUnnamedEnumeratorA2",
|
|
// xCHECK-NEXT: key.sourcetext: "FooHelperUnnamedEnumeratorA2",
|
|
// xCHECK-NEXT: key.description: "FooHelperUnnamedEnumeratorA2",
|
|
// xCHECK-NEXT: key.typename: "Int",
|
|
// xCHECK-NEXT: key.context: source.codecompletion.context.othermodule,
|
|
// xCHECK-NEXT: key.moduleimportdepth: 4,
|
|
// xCHECK-NEXT: key.num_bytes_to_erase: 0,
|
|
// xCHECK-NOT: key.modulename
|
|
// xCHECK: key.modulename: "FooHelper"
|
|
// xCHECK-NEXT: },
|