mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[PreCheck] Filter out macro declarations from result set
Move logic from `ConstraintGenerator::visitOverloadedDeclRefExpr` to pre-check to avoid including macro declarations referenced without `#`. This means that pre-checking would synthesize `TypeExpr` in situations when there is a type reference that is shadowed by a stdlib macro. Resolves: https://github.com/apple/swift/issues/67815 Resolves: rdar://114796811
This commit is contained in:
@@ -1658,8 +1658,7 @@ namespace {
|
|||||||
TVO_CanBindToLValue | TVO_CanBindToNoEscape);
|
TVO_CanBindToLValue | TVO_CanBindToNoEscape);
|
||||||
ArrayRef<ValueDecl*> decls = expr->getDecls();
|
ArrayRef<ValueDecl*> decls = expr->getDecls();
|
||||||
SmallVector<OverloadChoice, 4> choices;
|
SmallVector<OverloadChoice, 4> choices;
|
||||||
bool anyMacros = false;
|
|
||||||
auto addChoices = [&](bool skipMacros) {
|
|
||||||
for (unsigned i = 0, n = decls.size(); i != n; ++i) {
|
for (unsigned i = 0, n = decls.size(); i != n; ++i) {
|
||||||
// If the result is invalid, skip it.
|
// If the result is invalid, skip it.
|
||||||
// FIXME: Note this as invalid, in case we don't find a solution,
|
// FIXME: Note this as invalid, in case we don't find a solution,
|
||||||
@@ -1667,32 +1666,15 @@ namespace {
|
|||||||
if (decls[i]->isInvalid())
|
if (decls[i]->isInvalid())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// If the result is a macro, skip it if we're supposed to.
|
|
||||||
if (skipMacros && isa<MacroDecl>(decls[i])) {
|
|
||||||
anyMacros = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
OverloadChoice choice =
|
OverloadChoice choice =
|
||||||
OverloadChoice(Type(), decls[i], expr->getFunctionRefKind());
|
OverloadChoice(Type(), decls[i], expr->getFunctionRefKind());
|
||||||
choices.push_back(choice);
|
choices.push_back(choice);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
addChoices(/*skipMacros=*/true);
|
|
||||||
|
|
||||||
if (choices.empty()) {
|
if (choices.empty()) {
|
||||||
// If there are no valid overloads, but we ignored some macros, add
|
|
||||||
// the macros. This improves recovery when the user forgot the leading
|
|
||||||
// '#'.
|
|
||||||
if (anyMacros) {
|
|
||||||
addChoices(/*skipMacros=*/false);
|
|
||||||
assert(!choices.empty());
|
|
||||||
} else {
|
|
||||||
// There are no suitable overloads. Just fail.
|
// There are no suitable overloads. Just fail.
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Record this overload set.
|
// Record this overload set.
|
||||||
CS.addOverloadSet(tv, choices, CurDC, locator);
|
CS.addOverloadSet(tv, choices, CurDC, locator);
|
||||||
|
|||||||
@@ -650,16 +650,12 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
|
|||||||
|
|
||||||
// FIXME: Need to refactor the way we build an AST node from a lookup result!
|
// FIXME: Need to refactor the way we build an AST node from a lookup result!
|
||||||
|
|
||||||
// If we have an unambiguous reference to a type decl, form a TypeExpr.
|
auto buildTypeExpr = [&](TypeDecl *D) -> Expr * {
|
||||||
if (Lookup.size() == 1 && UDRE->getRefKind() == DeclRefKind::Ordinary &&
|
|
||||||
isa<TypeDecl>(Lookup[0].getValueDecl())) {
|
|
||||||
auto *D = cast<TypeDecl>(Lookup[0].getValueDecl());
|
|
||||||
// FIXME: This is odd.
|
// FIXME: This is odd.
|
||||||
if (isa<ModuleDecl>(D)) {
|
if (isa<ModuleDecl>(D)) {
|
||||||
return new (Context) DeclRefExpr(D, UDRE->getNameLoc(),
|
return new (Context) DeclRefExpr(
|
||||||
/*Implicit=*/false,
|
D, UDRE->getNameLoc(),
|
||||||
AccessSemantics::Ordinary,
|
/*Implicit=*/false, AccessSemantics::Ordinary, D->getInterfaceType());
|
||||||
D->getInterfaceType());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *LookupDC = Lookup[0].getDeclContext();
|
auto *LookupDC = Lookup[0].getDeclContext();
|
||||||
@@ -668,12 +664,19 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
|
|||||||
UDRE->getNameLoc(), D, LookupDC,
|
UDRE->getNameLoc(), D, LookupDC,
|
||||||
// It might happen that LookupDC is null if this is checking
|
// It might happen that LookupDC is null if this is checking
|
||||||
// synthesized code, in that case, don't map the type into context,
|
// synthesized code, in that case, don't map the type into context,
|
||||||
// but return as is -- the synthesis should ensure the type is correct.
|
// but return as is -- the synthesis should ensure the type is
|
||||||
|
// correct.
|
||||||
LookupDC ? LookupDC->mapTypeIntoContext(D->getInterfaceType())
|
LookupDC ? LookupDC->mapTypeIntoContext(D->getInterfaceType())
|
||||||
: D->getInterfaceType());
|
: D->getInterfaceType());
|
||||||
} else {
|
} else {
|
||||||
return TypeExpr::createForDecl(UDRE->getNameLoc(), D, LookupDC);
|
return TypeExpr::createForDecl(UDRE->getNameLoc(), D, LookupDC);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// If we have an unambiguous reference to a type decl, form a TypeExpr.
|
||||||
|
if (Lookup.size() == 1 && UDRE->getRefKind() == DeclRefKind::Ordinary &&
|
||||||
|
isa<TypeDecl>(Lookup[0].getValueDecl())) {
|
||||||
|
return buildTypeExpr(cast<TypeDecl>(Lookup[0].getValueDecl()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AllDeclRefs) {
|
if (AllDeclRefs) {
|
||||||
@@ -710,6 +713,24 @@ Expr *TypeChecker::resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Filter out macro declarations without `#` if there are valid
|
||||||
|
// non-macro results.
|
||||||
|
if (llvm::any_of(ResultValues,
|
||||||
|
[](const ValueDecl *D) { return !isa<MacroDecl>(D); })) {
|
||||||
|
ResultValues.erase(
|
||||||
|
llvm::remove_if(ResultValues,
|
||||||
|
[](const ValueDecl *D) { return isa<MacroDecl>(D); }),
|
||||||
|
ResultValues.end());
|
||||||
|
|
||||||
|
// If there is only one type reference in results, let's handle
|
||||||
|
// this in a special way.
|
||||||
|
if (ResultValues.size() == 1 &&
|
||||||
|
UDRE->getRefKind() == DeclRefKind::Ordinary &&
|
||||||
|
isa<TypeDecl>(ResultValues.front())) {
|
||||||
|
return buildTypeExpr(cast<TypeDecl>(ResultValues.front()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return buildRefExpr(ResultValues, DC, UDRE->getNameLoc(),
|
return buildRefExpr(ResultValues, DC, UDRE->getNameLoc(),
|
||||||
UDRE->isImplicit(), UDRE->getFunctionRefKind());
|
UDRE->isImplicit(), UDRE->getFunctionRefKind());
|
||||||
}
|
}
|
||||||
|
|||||||
38
test/Constraints/observable_macro_shadowing.swift
Normal file
38
test/Constraints/observable_macro_shadowing.swift
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// RUN: %empty-directory(%t/src)
|
||||||
|
// RUN: %empty-directory(%t/sdk)
|
||||||
|
// RUN: split-file %s %t/src
|
||||||
|
|
||||||
|
// RUN: %target-swift-frontend -emit-module %t/src/Test.swift \
|
||||||
|
// RUN: -module-name Test -swift-version 5 -enable-library-evolution \
|
||||||
|
// RUN: -emit-module-path %t/Test.swiftmodule
|
||||||
|
|
||||||
|
// RUN: %target-swift-frontend -typecheck %t/src/main.swift \
|
||||||
|
// RUN: -module-name main -I %t -verify
|
||||||
|
|
||||||
|
// REQUIRES: swift_swift_parser
|
||||||
|
// REQUIRES: observation
|
||||||
|
|
||||||
|
//--- Test.swift
|
||||||
|
|
||||||
|
public protocol ObservableConvertibleType {
|
||||||
|
associatedtype Element
|
||||||
|
}
|
||||||
|
|
||||||
|
public protocol ObservableType : ObservableConvertibleType {}
|
||||||
|
|
||||||
|
public class Observable<Element> : ObservableType {
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ObservableType {
|
||||||
|
public static func empty() -> Observable<Element> { fatalError() }
|
||||||
|
}
|
||||||
|
|
||||||
|
//--- main.swift
|
||||||
|
import Test
|
||||||
|
import Observation
|
||||||
|
|
||||||
|
extension Observable {
|
||||||
|
func test() -> Observable<Bool> {
|
||||||
|
return Observable<Bool>.empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,13 +9,11 @@
|
|||||||
@freestanding(expression) public macro ConcretePrint(_ value: Any) = #externalMacro(module: "MacroDefinition", type: "PrintMacro")
|
@freestanding(expression) public macro ConcretePrint(_ value: Any) = #externalMacro(module: "MacroDefinition", type: "PrintMacro")
|
||||||
@freestanding(expression) public macro MultiPrint(_ value: Any) = #externalMacro(module: "MacroDefinition", type: "PrintMacro")
|
@freestanding(expression) public macro MultiPrint(_ value: Any) = #externalMacro(module: "MacroDefinition", type: "PrintMacro")
|
||||||
|
|
||||||
public struct Printer<Value> {
|
public struct Printer<Value> { // expected-note {{generic type 'Printer' declared here}}
|
||||||
init(_: (Value) -> Void) {}
|
init(_: (Value) -> Void) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct MultiPrinter<T, U> {
|
public struct MultiPrinter<T, U> { // expected-note {{generic type 'MultiPrinter' declared here}}
|
||||||
// expected-note@-1 {{'T' declared as parameter to type 'MultiPrinter'}}
|
|
||||||
// expected-note@-2 {{'U' declared as parameter to type 'MultiPrinter'}}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typealias Print = Printer
|
typealias Print = Printer
|
||||||
@@ -34,7 +32,7 @@ struct Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let _ = Print<Object, Int> {
|
let _ = Print<Object, Int> {
|
||||||
// expected-error@-1 {{generic type 'Print' specialized with too many type parameters (got 2, but expected 1)}}
|
// expected-error@-1 {{generic type 'Printer' specialized with too many type parameters (got 2, but expected 1)}}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = OtherPrint<Object> { // Ok
|
let _ = OtherPrint<Object> { // Ok
|
||||||
@@ -42,14 +40,12 @@ struct Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let _ = ConcretePrint<Object> { // expected-error {{cannot specialize non-generic type 'ConcretePrint' (aka 'Printer<Any>')}}
|
let _ = ConcretePrint<Object> { // expected-error {{cannot specialize non-generic type 'ConcretePrint' (aka 'Printer<Any>')}}
|
||||||
compute(root: $0, \.prop) // expected-error {{value of type 'Any' has no member 'prop'}}
|
// expected-error@-1 {{cannot infer type of closure parameter '$0' without a type annotation}}
|
||||||
// expected-note@-1 {{cast 'Any' to 'AnyObject' or use 'as!' to force downcast to a more specific type to access members}}
|
compute(root: $0, \.prop)
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = MultiPrint<Int>()
|
let _ = MultiPrint<Int>()
|
||||||
// expected-error@-1 {{generic type 'MultiPrint' specialized with too few type parameters (got 1, but expected 2)}}
|
// expected-error@-1 {{generic type 'MultiPrinter' specialized with too few type parameters (got 1, but expected 2)}}
|
||||||
// expected-error@-2 {{generic parameter 'T' could not be inferred}}
|
|
||||||
// expected-error@-3 {{generic parameter 'U' could not be inferred}}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func compute<R, V>(root: R, _: KeyPath<R, V>) {}
|
func compute<R, V>(root: R, _: KeyPath<R, V>) {}
|
||||||
|
|||||||
Reference in New Issue
Block a user