mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Re-implement operator and precedencegroup decl lookup to use `namelookup::getAllImports` and existing decl shadowing logic. This allows us to find operator decls through `@_exported` imports, prefer operator decls defined in the same module over imported decls, and fixes a couple of other subtle issues. Because this new implementation is technically source breaking, as we can find multiple results where we used to only find one result, it's placed behind the new Frontend flag `-enable-new-operator-lookup` (with the aim of enabling it by default when we get a new language mode). However the new logic will always be used if the result is unambiguous. This means that e.g `@_exported` operators will be instantly available as long as there's only one candidate. If multiple candidates are found, we fall back to the old logic. Resolves SR-12132. Resolves rdar://59198796.
114 lines
4.8 KiB
Swift
114 lines
4.8 KiB
Swift
// RUN: %empty-directory(%t)
|
|
|
|
// RUN: %target-swift-frontend -emit-module %S/Inputs/lookup_moduleD.swift -module-name D -o %t -I %t
|
|
// RUN: %target-swift-frontend -emit-module %S/Inputs/lookup_moduleC.swift -module-name C -o %t -I %t
|
|
// RUN: %target-swift-frontend -emit-module %S/Inputs/lookup_moduleB.swift -module-name B -o %t -I %t
|
|
// RUN: %target-swift-frontend -emit-module %S/Inputs/lookup_moduleA.swift -module-name A -o %t -I %t
|
|
// RUN: %target-swift-frontend -emit-module %S/Inputs/lookup_module_exportsAC.swift -module-name ExportsAC -o %t -I %t
|
|
// RUN: %target-swift-frontend -typecheck -verify -primary-file %s %S/Inputs/lookup_other.swift %S/Inputs/lookup_other2.swift %S/Inputs/lookup_other_noncompat.swift -I %t -enable-new-operator-lookup
|
|
|
|
import ExportsAC
|
|
import B
|
|
|
|
infix operator ^^^ : DeclaredAcrossFiles
|
|
func ^^^ (lhs: Int, rhs: Int) -> Int { 0 }
|
|
func &&& (lhs: Int, rhs: Int) -> Int { 0 }
|
|
|
|
// The operator decl >>> is declared in module A, which we should be able to
|
|
// see through ExportsAC.
|
|
prefix func >>> (rhs: Double) {}
|
|
|
|
// Make sure we can also see precedencegroups in module A through ExportsAC.
|
|
infix operator ^^^^ : DeclaredInModuleA
|
|
|
|
// The operator decl for ??? is declared in both modules A and B, but has the
|
|
// same default precedence group in both, so there's no ambiguity.
|
|
func ??? (lhs: Int, rhs: Int) {}
|
|
|
|
// Same for ???!, declared in modules ExportsAC and B, but has the same
|
|
// precedence group in both.
|
|
func ???! (lhs: Int, rhs: Int) {}
|
|
|
|
// The operator decl for ???? is declared in both modules A and B, and has a
|
|
// different precedence group in each. Therefore ambiguous.
|
|
func ???? (lhs: Int, rhs: Int) {} // expected-error {{ambiguous operator declarations found for operator}}
|
|
|
|
// Same for ????!, declared in both modules ExportsAC and B, and has a different
|
|
// precedence group in each. Therefore ambiguous.
|
|
func ????! (lhs: Int, rhs: Int) {} // expected-error {{ambiguous operator declarations found for operator}}
|
|
|
|
// The precedencegroup is declared in both modules A and B, therefore ambiguous.
|
|
infix operator <?> : DeclaredInModulesAB // expected-error {{multiple precedence groups found}}
|
|
|
|
// The precedencegroup is declared in both modules ExportsAC and B, therefore
|
|
// ambiguous.
|
|
infix operator <!> : DeclaredInModulesBExportsAC // expected-error {{multiple precedence groups found}}
|
|
|
|
// While this precedencegroup is declared in both modules A and B, it's also
|
|
// declared in this module, which therefore shadows those decls.
|
|
infix operator <??> : DeclaredInModulesABShadowed
|
|
|
|
// The operator decl for <? is declared in both modules A and B, but there's no
|
|
// meaningful difference between the declarations, so legal.
|
|
postfix func <? (lhs: Int) {}
|
|
|
|
// Same thing, <! is declared in both modules ExportsAC and B, but there's no
|
|
// meaningful difference between the declarations, so legal.
|
|
postfix func <! (lhs: Int) {}
|
|
|
|
// This precedencegroup is declared in both modules A and ExportsAC, but the
|
|
// latter shadows the former.
|
|
infix operator <???> : ShadowsModuleA
|
|
|
|
// This precedencegroup is declared in modules A, C, and ExportsAC, but the
|
|
// latter shadows both of the former.
|
|
infix operator <????> : ShadowsModulesAC
|
|
|
|
// This operator decl is declared in modules A, C, and ExportsAC, but the
|
|
// latter shadows both of the former.
|
|
func ????? (lhs: Int, rhs: Int) {}
|
|
|
|
// This operator decl is declared in modules A, C, and ExportsAC, but the
|
|
// latter shadows both of the former, despite them having different
|
|
// precedencegroups.
|
|
func ?????? (lhs: Int, rhs: Int) {}
|
|
|
|
// Module D is imported through exports in both lookup_other and lookup_other2.
|
|
// Make sure we correctly handle visiting the same module twice.
|
|
infix operator <> : DeclaredInModuleD
|
|
|
|
// Also declared in lookup_other.
|
|
precedencegroup RedeclaredInModule {}
|
|
// expected-error@-1 {{precedence group redeclared}}
|
|
// expected-note@-2 {{found this matching precedence group}}
|
|
|
|
infix operator *** : RedeclaredInModule // expected-error {{multiple precedence groups found}}
|
|
|
|
func testOperatorLookup() {
|
|
// In lookup_other, DeclaredAcrossFiles is left associative, whereas in
|
|
// module B it is non-associative. Make sure we use lookup_other's.
|
|
_ = 5 ^^^ 5 ^^^ 5 // Okay.
|
|
|
|
// Same for &&&, in lookup_other it is declared as left associative.
|
|
_ = 5 &&& 5 &&& 5
|
|
|
|
// The operator >>> is declared in module A, which we should be able to see
|
|
// through ExportsAC.
|
|
>>>1
|
|
|
|
// We've been evil and overriden TernaryPrecedence in both modules A and B.
|
|
// Make sure we emit an ambiguity error without emitting a 'broken stdlib'
|
|
// error.
|
|
true ? () : () // expected-error {{multiple precedence groups found}}
|
|
}
|
|
|
|
precedencegroup CastingPrecedence {
|
|
lowerThan: AssignmentPrecedence
|
|
}
|
|
|
|
func testBuiltinPrecedenceGroupOverriding() {
|
|
// Evil, but allowed.
|
|
var x = 0
|
|
x = 0 as Int // expected-error {{cannot convert value of type '()' to type 'Int' in coercion}}
|
|
}
|