Skip to content

Commit

Permalink
Add unit test for overload selection
Browse files Browse the repository at this point in the history
  • Loading branch information
Jumhyn committed Oct 9, 2023
1 parent 14a2255 commit 970aed5
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 1 deletion.
3 changes: 2 additions & 1 deletion unittests/Sema/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ add_swift_unittest(swiftSemaTests
ConstraintSimplificationTests.cpp
UnresolvedMemberLookupTests.cpp
PlaceholderTypeInferenceTests.cpp
SolutionFilteringTests.cpp)
SolutionFilteringTests.cpp
KeypathFunctionConversionTests.cpp)

target_link_libraries(swiftSemaTests
PRIVATE
Expand Down
127 changes: 127 additions & 0 deletions unittests/Sema/KeypathFunctionConversionTests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
//===--- 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 *stringTypeDecl = getStdlibNominalTypeDecl("String");
auto *boolTypeDecl = getStdlibNominalTypeDecl("Bool");
auto boolType = boolTypeDecl->getDeclaredType();
auto boolOptType = OptionalType::get(boolType);
auto stringType = stringTypeDecl->getDeclaredType();

auto voidType = TupleType::get({}, Context);

auto *genericParam1 = new (Context) GenericTypeParamDecl(
DC, Context.getIdentifier("T"), SourceLoc(), SourceLoc(), 0, 0, false);
auto genericType1 =
genericParam1->getDeclaredInterfaceType()->getAs<GenericTypeParamType>();

auto *genericParam2 = new (Context) GenericTypeParamDecl(
DC, Context.getIdentifier("U"), SourceLoc(), SourceLoc(), 0, 1, false);
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 = new (Context) ParamDecl(
SourceLoc(), SourceLoc(), Identifier(), SourceLoc(), Identifier(), DC);
genericFnParamDecl->setSpecifier(ParamSpecifier::Default);
auto *genericFnParamList = ParameterList::create(
Context, SourceLoc(), {genericFnParamDecl}, SourceLoc());
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}, voidType)
->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 = new (Context) ParamDecl(
SourceLoc(), SourceLoc(), Identifier(), SourceLoc(), Identifier(), DC);
concreteFnParamDecl->setSpecifier(ParamSpecifier::Default);
auto *concreteFnParamList = ParameterList::create(
Context, SourceLoc(), {concreteFnParamDecl}, SourceLoc());
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}, voidType)
->withExtInfo(AnyFunctionType::ExtInfo());
concreteFnDecl->setInterfaceType(concreteFnTy);

// \String.isEmpty
auto *stringDRE = TypeExpr::createImplicitForDecl(
DeclNameLoc(), stringTypeDecl, 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(), llvm::None, false);
llvm::SmallVector<ValueDecl *, 2> fDecls = {genericFnDecl, concreteFnDecl};
auto *fDRE = new (Context) OverloadedDeclRefExpr(
fDecls, DeclNameLoc(), FunctionRefKind::SingleApply, false);
auto *callExpr = CallExpr::create(Context, fDRE, argList, false);

Expr *target = callExpr;
ConstraintSystem cs(DC, ConstraintSystemOptions());
ASSERT_FALSE(cs.preCheckExpression(target, DC, false, true));
auto *expr = cs.generateConstraints(callExpr, DC);
ASSERT_TRUE(expr);

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

0 comments on commit 970aed5

Please sign in to comment.