mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
FunctionRefKind was originally designed to represent the handling needed for argument labels on function references, in which the unapplied and compound cases are effectively the same. However it has since been adopted in a bunch of other places where the spelling of the function reference is entirely orthogonal to the application level. Split out the application level from the "is compound" bit. Should be NFC. I've left some FIXMEs for non-NFC changes that I'll address in a follow-up.
126 lines
5.9 KiB
C++
126 lines
5.9 KiB
C++
//===--- UnresolvedMemberLookupTests.cpp --------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "SemaFixture.h"
|
|
#include "swift/AST/GenericParamList.h"
|
|
#include "swift/Sema/ConstraintSystem.h"
|
|
|
|
using namespace swift;
|
|
using namespace swift::unittest;
|
|
using namespace swift::constraints;
|
|
|
|
/// Even in the face of a more permissive conversion that might be chosen based
|
|
/// on other ranking rules (e.g., the overload required is non-generic), ensure
|
|
/// that we will select the solution which requires the more narrow conversion
|
|
/// (e.g., the overload *is* generic).
|
|
TEST_F(SemaTest, TestKeypathFunctionConversionPrefersNarrowConversion) {
|
|
auto boolType = getStdlibType("Bool");
|
|
auto boolOptType = OptionalType::get(boolType);
|
|
auto stringType = getStdlibType("String");
|
|
|
|
auto *genericParam1 = GenericTypeParamDecl::createImplicit(
|
|
DC, Context.getIdentifier("T"), 0, 0, GenericTypeParamKind::Type);
|
|
auto genericType1 =
|
|
genericParam1->getDeclaredInterfaceType()->getAs<GenericTypeParamType>();
|
|
|
|
auto *genericParam2 = GenericTypeParamDecl::createImplicit(
|
|
DC, Context.getIdentifier("T"), 0, 1, GenericTypeParamKind::Type);
|
|
auto genericType2 =
|
|
genericParam2->getDeclaredInterfaceType()->getAs<GenericTypeParamType>();
|
|
|
|
auto declName = DeclName(Context, Context.getIdentifier("f"), {Identifier()});
|
|
|
|
// func f<T, U>(_: (T) -> U))
|
|
auto innerGenericFnParam = AnyFunctionType::Param(genericType1);
|
|
auto genericFnParamTy = FunctionType::get({innerGenericFnParam}, genericType2)
|
|
->withExtInfo(AnyFunctionType::ExtInfo());
|
|
auto *genericFnParamDecl = ParamDecl::createImplicit(
|
|
Context, Identifier(), Identifier(), genericFnParamTy, DC);
|
|
genericFnParamDecl->setSpecifier(ParamSpecifier::Default);
|
|
auto *genericFnParamList =
|
|
ParameterList::createWithoutLoc(genericFnParamDecl);
|
|
llvm::SmallVector<GenericTypeParamDecl *, 2> genericParams = {genericParam1,
|
|
genericParam2};
|
|
auto *genericParamList =
|
|
GenericParamList::create(Context, SourceLoc(), {}, SourceLoc());
|
|
auto genericFnDecl = FuncDecl::create(
|
|
Context, SourceLoc(), StaticSpellingKind::None, SourceLoc(), declName,
|
|
SourceLoc(), /*async=*/false, SourceLoc(), /*throws=*/false, SourceLoc(),
|
|
nullptr, genericParamList, genericFnParamList, nullptr, DC);
|
|
auto genericFnParam = AnyFunctionType::Param(genericFnParamTy);
|
|
llvm::SmallVector<GenericTypeParamType *, 2> genericTypeParams = {
|
|
genericType1, genericType2};
|
|
auto genericSig = GenericSignature::get(genericTypeParams, {});
|
|
auto genericFnTy = GenericFunctionType::get(genericSig, {genericFnParam},
|
|
Context.TheEmptyTupleType)
|
|
->withExtInfo(AnyFunctionType::ExtInfo());
|
|
genericFnDecl->setInterfaceType(genericFnTy);
|
|
|
|
// func f(_: (String) -> Bool?)
|
|
auto innerConcreteFnParam = AnyFunctionType::Param(stringType);
|
|
auto concreteFnParamTy =
|
|
FunctionType::get({innerConcreteFnParam}, boolOptType)
|
|
->withExtInfo(AnyFunctionType::ExtInfo());
|
|
auto *concreteFnParamDecl = ParamDecl::createImplicit(
|
|
Context, Identifier(), Identifier(), concreteFnParamTy, DC);
|
|
concreteFnParamDecl->setSpecifier(ParamSpecifier::Default);
|
|
auto *concreteFnParamList =
|
|
ParameterList::createWithoutLoc(concreteFnParamDecl);
|
|
auto concreteFnDecl = FuncDecl::create(
|
|
Context, SourceLoc(), StaticSpellingKind::None, SourceLoc(), declName,
|
|
SourceLoc(), /*async=*/false, SourceLoc(), /*throws=*/false, SourceLoc(),
|
|
nullptr, nullptr, concreteFnParamList, nullptr, DC);
|
|
auto concreteFnParam = AnyFunctionType::Param(concreteFnParamTy);
|
|
auto concreteFnTy =
|
|
FunctionType::get({concreteFnParam}, Context.TheEmptyTupleType)
|
|
->withExtInfo(AnyFunctionType::ExtInfo());
|
|
concreteFnDecl->setInterfaceType(concreteFnTy);
|
|
|
|
// \String.isEmpty
|
|
auto *stringDRE = TypeExpr::createImplicitForDecl(
|
|
DeclNameLoc(), stringType->getAnyNominal(), Context.getStdlibModule(),
|
|
stringType);
|
|
auto *isEmptyDE = new (Context) UnresolvedDotExpr(
|
|
stringDRE, SourceLoc(), DeclNameRef(Context.getIdentifier("isEmpty")),
|
|
DeclNameLoc(), false);
|
|
auto *kpExpr = KeyPathExpr::createParsed(Context, SourceLoc(), isEmptyDE,
|
|
nullptr, false);
|
|
|
|
// f(\String.isEmpty)
|
|
auto kpArg = Argument(SourceLoc(), Identifier(), kpExpr);
|
|
auto *argList = ArgumentList::create(Context, SourceLoc(), {kpArg},
|
|
SourceLoc(), std::nullopt, false);
|
|
llvm::SmallVector<ValueDecl *, 2> fDecls = {genericFnDecl, concreteFnDecl};
|
|
auto *fDRE = new (Context) OverloadedDeclRefExpr(
|
|
fDecls, DeclNameLoc(), FunctionRefInfo::singleBaseNameApply(), false);
|
|
auto *callExpr = CallExpr::create(Context, fDRE, argList, false);
|
|
|
|
ConstraintSystem cs(DC, ConstraintSystemOptions());
|
|
auto target = SyntacticElementTarget(callExpr, DC, CTP_Unused, Type(),
|
|
/*isDiscarded*/ true);
|
|
ASSERT_FALSE(cs.preCheckTarget(target));
|
|
ASSERT_FALSE(cs.generateConstraints(target));
|
|
|
|
SmallVector<Solution, 2> solutions;
|
|
cs.solve(solutions);
|
|
|
|
// We should have a solution.
|
|
ASSERT_EQ(solutions.size(), 1u);
|
|
|
|
auto &solution = solutions[0];
|
|
auto *locator = cs.getConstraintLocator(fDRE);
|
|
auto choice = solution.getOverloadChoice(locator).choice;
|
|
|
|
// We should select the generic function since it requires 'less' conversion.
|
|
ASSERT_EQ(choice.getDecl(), genericFnDecl);
|
|
}
|