File: KeypathFunctionConversionTests.cpp

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (125 lines) | stat: -rw-r--r-- 5,870 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
//===--- 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);
  auto genericType1 =
      genericParam1->getDeclaredInterfaceType()->getAs<GenericTypeParamType>();

  auto *genericParam2 = GenericTypeParamDecl::createImplicit(
      DC, Context.getIdentifier("T"), 0, 1);
  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(), 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));
  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);
}