Skip to content

Commit 8dcd1c6

Browse files
committed
Clean up CSSimplify and CSApply for func callAsFunction.
- CSSimplify: get type variable for `func callAsFunction` member by generating a member constraint (with the potentially-lvalue base `type2`) rather than manually constructing an overload set. - CSApply: remove hack for diagnosing invalid `mutating callAsFunction` calls. - Add todo comments for improving `ConstraintSystem::simplifyDynamicCallableApplicableFnConstraint`.
1 parent 35607ed commit 8dcd1c6

File tree

3 files changed

+51
-74
lines changed

3 files changed

+51
-74
lines changed

lib/Sema/CSApply.cpp

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6775,17 +6775,6 @@ static Expr *finishApplyCallAsFunctionMethod(
67756775
auto &cs = rewriter.cs;
67766776
auto *fn = apply->getFn();
67776777
auto choice = selected.choice;
6778-
auto *method = dyn_cast<FuncDecl>(choice.getDecl());
6779-
auto selfParam = method->getImplicitSelfDecl();
6780-
6781-
// Diagnose `mutating` method call on immutable value.
6782-
if (!cs.getType(fn)->hasLValueType() && selfParam->isInOut()) {
6783-
AssignmentFailure failure(fn, cs, fn->getLoc(),
6784-
diag::cannot_pass_rvalue_mutating_subelement,
6785-
diag::cannot_pass_rvalue_mutating);
6786-
failure.diagnose();
6787-
return nullptr;
6788-
}
67896778
// Create direct reference to `callAsFunction` method.
67906779
bool isDynamic = choice.getKind() == OverloadChoiceKind::DeclViaDynamic;
67916780
auto *declRef = rewriter.buildMemberRef(

lib/Sema/CSSimplify.cpp

Lines changed: 44 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -5987,6 +5987,7 @@ ConstraintSystem::simplifyApplicableFnConstraint(
59875987
Type type2,
59885988
TypeMatchOptions flags,
59895989
ConstraintLocatorBuilder locator) {
5990+
auto &ctx = getASTContext();
59905991

59915992
// By construction, the left hand side is a type that looks like the
59925993
// following: $T1 -> $T2.
@@ -6011,6 +6012,15 @@ ConstraintSystem::simplifyApplicableFnConstraint(
60116012
}
60126013
}
60136014

6015+
// Before stripping lvalue-ness and optional types, save original type for
6016+
// handling `func callAsFunction` and `@dynamicCallable` applications.
6017+
// This supports the following cases:
6018+
// - Generating constraints for `mutating func callAsFunction`. The nominal
6019+
// type (`type2`) should be an lvalue type.
6020+
// - Extending `Optional` itself with `func callAsFunction` or
6021+
// `@dynamicCallable` functionality. Optional types are stripped below if
6022+
// `shouldAttemptFixes()` is true.
6023+
auto *origType2 = type2->getDesugaredType();
60146024
// Drill down to the concrete type on the right hand side.
60156025
type2 = getFixedTypeRecursive(type2, flags, /*wantRValue=*/true);
60166026
auto desugar2 = type2->getDesugaredType();
@@ -6080,10 +6090,33 @@ ConstraintSystem::simplifyApplicableFnConstraint(
60806090
ConstraintLocatorBuilder outerLocator =
60816091
getConstraintLocator(anchor, parts, locator.getSummaryFlags());
60826092

6083-
// Before stripping optional types, save original type for handling
6084-
// @dynamicCallable applications. This supports the fringe case where
6085-
// `Optional` itself is extended with @dynamicCallable functionality.
6086-
auto origType2 = desugar2;
6093+
// Handle applications of types with `callAsFunction` methods.
6094+
// Do this before stripping optional types below, when `shouldAttemptFixes()`
6095+
// is true.
6096+
auto hasCallAsFunctionMethods =
6097+
desugar2->mayHaveMembers() &&
6098+
llvm::any_of(lookupMember(desugar2, DeclName(ctx.Id_callAsFunction)),
6099+
[](LookupResultEntry entry) {
6100+
return isa<FuncDecl>(entry.getValueDecl());
6101+
});
6102+
if (hasCallAsFunctionMethods) {
6103+
auto memberLoc = getConstraintLocator(
6104+
outerLocator.withPathElement(ConstraintLocator::Member));
6105+
// Add a `callAsFunction` member constraint, binding the member type to a
6106+
// type variable.
6107+
auto memberTy = createTypeVariable(memberLoc, TVO_CanBindToLValue |
6108+
TVO_CanBindToNoEscape |
6109+
TVO_CanBindToInOut);
6110+
addValueMemberConstraint(origType2, DeclName(ctx.Id_callAsFunction),
6111+
memberTy, DC, FunctionRefKind::SingleApply,
6112+
/*outerAlternatives*/ {}, locator);
6113+
// Add new applicable function constraint based on the member type
6114+
// variable.
6115+
addConstraint(ConstraintKind::ApplicableFunction, func1, memberTy,
6116+
locator);
6117+
return SolutionKind::Solved;
6118+
}
6119+
60876120
unsigned unwrapCount = 0;
60886121
if (shouldAttemptFixes()) {
60896122
// If we have an optional type, try forcing it to see if that
@@ -6165,60 +6198,6 @@ ConstraintSystem::simplifyApplicableFnConstraint(
61656198
return simplified;
61666199
}
61676200

6168-
// Handle applications of types with `callAsFunction` methods.
6169-
if (desugar2->mayHaveMembers()) {
6170-
auto &ctx = getASTContext();
6171-
// Get all `callAsFunction` methods of the nominal type.
6172-
// Note: Consider caching `callAsFunction` methods.
6173-
SmallVector<FuncDecl *, 4> callMethods;
6174-
auto candidates = lookupMember(desugar2, DeclName(ctx.Id_callAsFunction));
6175-
for (auto entry : candidates) {
6176-
auto callMethod = dyn_cast<FuncDecl>(entry.getValueDecl());
6177-
if (!callMethod)
6178-
continue;
6179-
callMethods.push_back(callMethod);
6180-
}
6181-
6182-
// Handle `callAsFunction` methods calls.
6183-
if (!callMethods.empty()) {
6184-
// Create a type variable for the `callAsFunction` method.
6185-
auto loc = getConstraintLocator(locator);
6186-
auto tv = createTypeVariable(loc, TVO_CanBindToLValue);
6187-
6188-
// Record the `callAsFunction` method overload set.
6189-
SmallVector<OverloadChoice, 4> choices;
6190-
for (auto candidate : callMethods) {
6191-
TC.validateDecl(candidate);
6192-
if (candidate->isInvalid()) continue;
6193-
choices.push_back(
6194-
OverloadChoice(type2, candidate, FunctionRefKind::SingleApply));
6195-
}
6196-
if (choices.empty()) return SolutionKind::Error;
6197-
addOverloadSet(tv, choices, DC, loc);
6198-
6199-
// Create type variables for each parameter type.
6200-
SmallVector<AnyFunctionType::Param, 4> tvParams;
6201-
for (unsigned i : range(func1->getNumParams())) {
6202-
auto param = func1->getParams()[i];
6203-
auto paramType = param.getPlainType();
6204-
6205-
auto *tvParam = createTypeVariable(loc, TVO_CanBindToNoEscape);
6206-
auto locatorBuilder =
6207-
locator.withPathElement(LocatorPathElt::getTupleElement(i));
6208-
addConstraint(ConstraintKind::ArgumentConversion, paramType,
6209-
tvParam, locatorBuilder);
6210-
tvParams.push_back(AnyFunctionType::Param(
6211-
tvParam, Identifier(), param.getParameterFlags()));
6212-
}
6213-
// Create target function type and an applicable function constraint.
6214-
AnyFunctionType *funcType =
6215-
FunctionType::get(tvParams, func1->getResult());
6216-
addConstraint(ConstraintKind::ApplicableFunction, funcType, tv, locator);
6217-
6218-
return SolutionKind::Solved;
6219-
}
6220-
}
6221-
62226201
// Handle applications of @dynamicCallable types.
62236202
return simplifyDynamicCallableApplicableFnConstraint(type1, origType2,
62246203
subflags, locator);
@@ -6359,6 +6338,13 @@ getDynamicCallableMethods(Type type, ConstraintSystem &CS,
63596338
return result;
63606339
}
63616340

6341+
// TODO: Refactor/simplify this function.
6342+
// - It should perform less duplicate work with its caller
6343+
// `ConstraintSystem::simplifyApplicableFnConstraint`.
6344+
// - It should generate a member constraint instead of manually forming an
6345+
// overload set for `func dynamicallyCall` candidates.
6346+
// - It should support `mutating func dynamicallyCall`. This should fall out of
6347+
// using member constraints with an lvalue base type.
63626348
ConstraintSystem::SolutionKind
63636349
ConstraintSystem::simplifyDynamicCallableApplicableFnConstraint(
63646350
Type type1,

test/Sema/call_as_function_simple.swift

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,12 @@ struct Mutating {
106106
}
107107
}
108108
func testMutating(_ x: Mutating, _ y: inout Mutating) {
109-
_ = x() // expected-error {{cannot use mutating member on immutable value: 'x' is a 'let' constant}}
110-
_ = x.callAsFunction() // expected-error {{cannot use mutating member on immutable value: 'x' is a 'let' constant}}
109+
// TODO: Improve this error to match the error using a direct `callAsFunction` member reference.
110+
// expected-error @+2 {{cannot call value of non-function type 'Mutating'}}
111+
// expected-error @+1 {{cannot invoke 'x' with no arguments}}
112+
_ = x()
113+
// expected-error @+1 {{cannot use mutating member on immutable value: 'x' is a 'let' constant}}
114+
_ = x.callAsFunction()
111115
_ = y()
112116
_ = y.callAsFunction()
113117
}
@@ -120,9 +124,7 @@ struct Inout {
120124
func testInout(_ x: Inout, _ arg: inout Int) {
121125
x(&arg)
122126
x.callAsFunction(&arg)
123-
// TODO: Improve this error to match the error using a direct `callAsFunction` member reference.
124-
// expected-error @+2 {{cannot invoke 'x' with an argument list of type '(Int)'}}
125-
// expected-error @+1 {{cannot call value of non-function type 'Inout'}}
127+
// expected-error @+1 {{passing value of type 'Int' to an inout parameter requires explicit '&'}}
126128
x(arg)
127129
// expected-error @+1 {{passing value of type 'Int' to an inout parameter requires explicit '&'}}
128130
x.callAsFunction(arg)

0 commit comments

Comments
 (0)