[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:
Pavel Yaskevich
2023-08-31 17:30:14 -07:00
parent 1305901318
commit cf257aa64a
4 changed files with 86 additions and 49 deletions

View File

@@ -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);

View File

@@ -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());
} }

View 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()
}
}

View File

@@ -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>) {}