|
| 1 | +//===--- UnresolvedMemberLookupTests.cpp --------------------------------===// |
| 2 | +// |
| 3 | +// This source file is part of the Swift.org open source project |
| 4 | +// |
| 5 | +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors |
| 6 | +// Licensed under Apache License v2.0 with Runtime Library Exception |
| 7 | +// |
| 8 | +// See https://swift.org/LICENSE.txt for license information |
| 9 | +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| 10 | +// |
| 11 | +//===----------------------------------------------------------------------===// |
| 12 | + |
| 13 | +#include "SemaFixture.h" |
| 14 | +#include "swift/AST/GenericParamList.h" |
| 15 | +#include "swift/Sema/ConstraintSystem.h" |
| 16 | + |
| 17 | +using namespace swift; |
| 18 | +using namespace swift::unittest; |
| 19 | +using namespace swift::constraints; |
| 20 | + |
| 21 | +/// Even in the face of a more permissive conversion that might be chosen based |
| 22 | +/// on other ranking rules (e.g., the overload required is non-generic), ensure |
| 23 | +/// that we will select the solution which requires the more narrow conversion |
| 24 | +/// (e.g., the overload *is* generic). |
| 25 | +TEST_F(SemaTest, TestKeypathFunctionConversionPrefersNarrowConversion) { |
| 26 | + auto *stringTypeDecl = getStdlibNominalTypeDecl("String"); |
| 27 | + auto *boolTypeDecl = getStdlibNominalTypeDecl("Bool"); |
| 28 | + auto boolType = boolTypeDecl->getDeclaredType(); |
| 29 | + auto boolOptType = OptionalType::get(boolType); |
| 30 | + auto stringType = stringTypeDecl->getDeclaredType(); |
| 31 | + |
| 32 | + auto voidType = TupleType::get({}, Context); |
| 33 | + |
| 34 | + auto *genericParam1 = new (Context) GenericTypeParamDecl( |
| 35 | + DC, Context.getIdentifier("T"), SourceLoc(), SourceLoc(), 0, 0, false); |
| 36 | + auto genericType1 = |
| 37 | + genericParam1->getDeclaredInterfaceType()->getAs<GenericTypeParamType>(); |
| 38 | + |
| 39 | + auto *genericParam2 = new (Context) GenericTypeParamDecl( |
| 40 | + DC, Context.getIdentifier("U"), SourceLoc(), SourceLoc(), 0, 1, false); |
| 41 | + auto genericType2 = |
| 42 | + genericParam2->getDeclaredInterfaceType()->getAs<GenericTypeParamType>(); |
| 43 | + |
| 44 | + auto declName = DeclName(Context, Context.getIdentifier("f"), {Identifier()}); |
| 45 | + |
| 46 | + // func f<T, U>(_: (T) -> U)) |
| 47 | + auto innerGenericFnParam = AnyFunctionType::Param(genericType1); |
| 48 | + auto genericFnParamTy = FunctionType::get({innerGenericFnParam}, genericType2) |
| 49 | + ->withExtInfo(AnyFunctionType::ExtInfo()); |
| 50 | + auto *genericFnParamDecl = new (Context) ParamDecl( |
| 51 | + SourceLoc(), SourceLoc(), Identifier(), SourceLoc(), Identifier(), DC); |
| 52 | + genericFnParamDecl->setSpecifier(ParamSpecifier::Default); |
| 53 | + auto *genericFnParamList = ParameterList::create( |
| 54 | + Context, SourceLoc(), {genericFnParamDecl}, SourceLoc()); |
| 55 | + llvm::SmallVector<GenericTypeParamDecl *, 2> genericParams = {genericParam1, |
| 56 | + genericParam2}; |
| 57 | + auto *genericParamList = |
| 58 | + GenericParamList::create(Context, SourceLoc(), {}, SourceLoc()); |
| 59 | + auto genericFnDecl = FuncDecl::create( |
| 60 | + Context, SourceLoc(), StaticSpellingKind::None, SourceLoc(), declName, |
| 61 | + SourceLoc(), /*async=*/false, SourceLoc(), /*throws=*/false, SourceLoc(), |
| 62 | + nullptr, genericParamList, genericFnParamList, nullptr, DC); |
| 63 | + auto genericFnParam = AnyFunctionType::Param(genericFnParamTy); |
| 64 | + llvm::SmallVector<GenericTypeParamType *, 2> genericTypeParams = { |
| 65 | + genericType1, genericType2}; |
| 66 | + auto genericSig = GenericSignature::get(genericTypeParams, {}); |
| 67 | + auto genericFnTy = |
| 68 | + GenericFunctionType::get(genericSig, {genericFnParam}, voidType) |
| 69 | + ->withExtInfo(AnyFunctionType::ExtInfo()); |
| 70 | + genericFnDecl->setInterfaceType(genericFnTy); |
| 71 | + |
| 72 | + // func f(_: (String) -> Bool?) |
| 73 | + auto innerConcreteFnParam = AnyFunctionType::Param(stringType); |
| 74 | + auto concreteFnParamTy = |
| 75 | + FunctionType::get({innerConcreteFnParam}, boolOptType) |
| 76 | + ->withExtInfo(AnyFunctionType::ExtInfo()); |
| 77 | + auto *concreteFnParamDecl = new (Context) ParamDecl( |
| 78 | + SourceLoc(), SourceLoc(), Identifier(), SourceLoc(), Identifier(), DC); |
| 79 | + concreteFnParamDecl->setSpecifier(ParamSpecifier::Default); |
| 80 | + auto *concreteFnParamList = ParameterList::create( |
| 81 | + Context, SourceLoc(), {concreteFnParamDecl}, SourceLoc()); |
| 82 | + auto concreteFnDecl = FuncDecl::create( |
| 83 | + Context, SourceLoc(), StaticSpellingKind::None, SourceLoc(), declName, |
| 84 | + SourceLoc(), /*async=*/false, SourceLoc(), /*throws=*/false, SourceLoc(), |
| 85 | + nullptr, nullptr, concreteFnParamList, nullptr, DC); |
| 86 | + auto concreteFnParam = AnyFunctionType::Param(concreteFnParamTy); |
| 87 | + auto concreteFnTy = FunctionType::get({concreteFnParam}, voidType) |
| 88 | + ->withExtInfo(AnyFunctionType::ExtInfo()); |
| 89 | + concreteFnDecl->setInterfaceType(concreteFnTy); |
| 90 | + |
| 91 | + // \String.isEmpty |
| 92 | + auto *stringDRE = TypeExpr::createImplicitForDecl( |
| 93 | + DeclNameLoc(), stringTypeDecl, Context.getStdlibModule(), stringType); |
| 94 | + auto *isEmptyDE = new (Context) UnresolvedDotExpr( |
| 95 | + stringDRE, SourceLoc(), DeclNameRef(Context.getIdentifier("isEmpty")), |
| 96 | + DeclNameLoc(), false); |
| 97 | + auto *kpExpr = KeyPathExpr::createParsed(Context, SourceLoc(), isEmptyDE, |
| 98 | + nullptr, false); |
| 99 | + |
| 100 | + // f(\String.isEmpty) |
| 101 | + auto kpArg = Argument(SourceLoc(), Identifier(), kpExpr); |
| 102 | + auto *argList = ArgumentList::create(Context, SourceLoc(), {kpArg}, |
| 103 | + SourceLoc(), llvm::None, false); |
| 104 | + llvm::SmallVector<ValueDecl *, 2> fDecls = {genericFnDecl, concreteFnDecl}; |
| 105 | + auto *fDRE = new (Context) OverloadedDeclRefExpr( |
| 106 | + fDecls, DeclNameLoc(), FunctionRefKind::SingleApply, false); |
| 107 | + auto *callExpr = CallExpr::create(Context, fDRE, argList, false); |
| 108 | + |
| 109 | + Expr *target = callExpr; |
| 110 | + ConstraintSystem cs(DC, ConstraintSystemOptions()); |
| 111 | + ASSERT_FALSE(cs.preCheckExpression(target, DC, false, true)); |
| 112 | + auto *expr = cs.generateConstraints(callExpr, DC); |
| 113 | + ASSERT_TRUE(expr); |
| 114 | + |
| 115 | + SmallVector<Solution, 2> solutions; |
| 116 | + cs.solve(solutions); |
| 117 | + |
| 118 | + // We should have a solution. |
| 119 | + ASSERT_EQ(solutions.size(), 1u); |
| 120 | + |
| 121 | + auto &solution = solutions[0]; |
| 122 | + auto *locator = cs.getConstraintLocator(fDRE); |
| 123 | + auto choice = solution.getOverloadChoice(locator).choice; |
| 124 | + |
| 125 | + // We should select the generic function since it requires 'less' conversion. |
| 126 | + ASSERT_EQ(choice.getDecl(), genericFnDecl); |
| 127 | +} |
0 commit comments