Skip to content

Commit a1b2811

Browse files
authored
Merge pull request #25359 from hamishknight/five-minute-argument
2 parents 978ec73 + e285916 commit a1b2811

File tree

8 files changed

+389
-117
lines changed

8 files changed

+389
-117
lines changed

include/swift/AST/Decl.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2700,7 +2700,11 @@ class ValueDecl : public Decl {
27002700
/// True if this is a C function that was imported as a member of a type in
27012701
/// Swift.
27022702
bool isImportAsMember() const;
2703-
2703+
2704+
/// Returns true if the declaration's interface type is a function type with a
2705+
/// curried self parameter.
2706+
bool hasCurriedSelf() const;
2707+
27042708
/// Get the decl for this value's opaque result type, if it has one.
27052709
OpaqueTypeDecl *getOpaqueResultTypeDecl() const;
27062710

@@ -7189,6 +7193,14 @@ inline bool ValueDecl::isImportAsMember() const {
71897193
return false;
71907194
}
71917195

7196+
inline bool ValueDecl::hasCurriedSelf() const {
7197+
if (auto *afd = dyn_cast<AbstractFunctionDecl>(this))
7198+
return afd->hasImplicitSelfDecl();
7199+
if (isa<EnumElementDecl>(this))
7200+
return true;
7201+
return false;
7202+
}
7203+
71927204
inline bool Decl::isPotentiallyOverridable() const {
71937205
if (isa<VarDecl>(this) ||
71947206
isa<SubscriptDecl>(this) ||

lib/Sema/CSDiagnostics.cpp

Lines changed: 177 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,159 @@ Optional<SelectedOverload> FailureDiagnostic::getChoiceFor(Expr *expr) const {
110110
return getOverloadChoiceIfAvailable(cs.getCalleeLocator(expr));
111111
}
112112

113+
Type FailureDiagnostic::resolveInterfaceType(Type type,
114+
bool reconstituteSugar) const {
115+
auto &cs = getConstraintSystem();
116+
auto resolvedType = type.transform([&](Type type) -> Type {
117+
if (auto *tvt = type->getAs<TypeVariableType>()) {
118+
// If this type variable is for a generic parameter, return that.
119+
if (auto *gp = tvt->getImpl().getGenericParameter())
120+
return gp;
121+
122+
// Otherwise resolve its fixed type, mapped out of context.
123+
if (auto fixed = cs.getFixedType(tvt))
124+
return resolveInterfaceType(fixed->mapTypeOutOfContext());
125+
126+
return cs.getRepresentative(tvt);
127+
}
128+
if (auto *dmt = type->getAs<DependentMemberType>()) {
129+
// For a dependent member, first resolve the base.
130+
auto newBase = resolveInterfaceType(dmt->getBase());
131+
132+
// Then reconstruct using its associated type.
133+
assert(dmt->getAssocType());
134+
return DependentMemberType::get(newBase, dmt->getAssocType());
135+
}
136+
return type;
137+
});
138+
139+
assert(!resolvedType->hasArchetype());
140+
return reconstituteSugar ? resolvedType->reconstituteSugar(/*recursive*/ true)
141+
: resolvedType;
142+
}
143+
144+
/// Given an apply expr, returns true if it is expected to have a direct callee
145+
/// overload, resolvable using `getChoiceFor`. Otherwise, returns false.
146+
static bool shouldHaveDirectCalleeOverload(const ApplyExpr *apply) {
147+
auto *fnExpr = apply->getFn()->getValueProvidingExpr();
148+
149+
// An apply of an apply/subscript doesn't have a direct callee.
150+
if (isa<ApplyExpr>(fnExpr) || isa<SubscriptExpr>(fnExpr))
151+
return false;
152+
153+
// Applies of closures don't have callee overloads.
154+
if (isa<ClosureExpr>(fnExpr))
155+
return false;
156+
157+
// If the optionality changes, there's no direct callee.
158+
if (isa<BindOptionalExpr>(fnExpr) || isa<ForceValueExpr>(fnExpr) ||
159+
isa<OptionalTryExpr>(fnExpr)) {
160+
return false;
161+
}
162+
163+
// If we have an intermediate cast, there's no direct callee.
164+
if (isa<ExplicitCastExpr>(fnExpr))
165+
return false;
166+
167+
// No direct callee for an if expr.
168+
if (isa<IfExpr>(fnExpr))
169+
return false;
170+
171+
// Assume that anything else would have a direct callee.
172+
return true;
173+
}
174+
175+
Optional<FunctionArgApplyInfo>
176+
FailureDiagnostic::getFunctionArgApplyInfo(ConstraintLocator *locator) const {
177+
auto &cs = getConstraintSystem();
178+
auto *anchor = locator->getAnchor();
179+
auto path = locator->getPath();
180+
181+
// Look for the apply-arg-to-param element in the locator's path. We may
182+
// have to look through other elements that are generated from an argument
183+
// conversion such as GenericArgument for an optional-to-optional conversion,
184+
// and OptionalPayload for a value-to-optional conversion.
185+
auto applyArgElt =
186+
std::find_if(path.rbegin(), path.rend(), [](LocatorPathElt elt) {
187+
return elt.getKind() == ConstraintLocator::ApplyArgToParam;
188+
});
189+
190+
if (applyArgElt == path.rend())
191+
return None;
192+
193+
assert(std::find_if(applyArgElt + 1, path.rend(), [](LocatorPathElt elt) {
194+
return elt.getKind() == ConstraintLocator::ApplyArgToParam;
195+
}) == path.rend() && "Multiple ApplyArgToParam components?");
196+
197+
// Form a new locator that ends at the apply-arg-to-param element, and
198+
// simplify it to get the full argument expression.
199+
auto argPath = path.drop_back(applyArgElt - path.rbegin());
200+
auto *argLocator = cs.getConstraintLocator(
201+
anchor, argPath, ConstraintLocator::getSummaryFlagsForPath(argPath));
202+
203+
auto *argExpr = simplifyLocatorToAnchor(cs, argLocator);
204+
205+
// If we were unable to simplify down to the argument expression, we don't
206+
// know what this is.
207+
if (!argExpr)
208+
return None;
209+
210+
ValueDecl *callee = nullptr;
211+
Type rawFnType;
212+
if (auto overload = getChoiceFor(anchor)) {
213+
// If we have resolved an overload for the callee, then use that to get the
214+
// function type and callee.
215+
if (auto *decl = overload->choice.getDeclOrNull())
216+
callee = decl;
217+
218+
rawFnType = overload->openedType;
219+
} else {
220+
// If we didn't resolve an overload for the callee, we must be dealing with
221+
// a call of an arbitrary function expr.
222+
auto *call = cast<CallExpr>(anchor);
223+
assert(!shouldHaveDirectCalleeOverload(call) &&
224+
"Should we have resolved a callee for this?");
225+
rawFnType = cs.getType(call->getFn())->getRValueType();
226+
}
227+
228+
auto *fnType = resolveType(rawFnType)->getAs<FunctionType>();
229+
if (!fnType)
230+
return None;
231+
232+
// Resolve the interface type for the function. Note that this may not be a
233+
// function type, for example it could be a generic parameter.
234+
Type fnInterfaceType;
235+
if (callee && callee->hasInterfaceType()) {
236+
// If we have a callee with an interface type, we can use it. This is
237+
// preferable to resolveInterfaceType, as this will allow us to get a
238+
// GenericFunctionType for generic decls.
239+
//
240+
// Note that it's possible to find a callee without an interface type. This
241+
// can happen for example with closure parameters, where the interface type
242+
// isn't set until the solution is applied. In that case, use
243+
// resolveInterfaceType.
244+
fnInterfaceType = callee->getInterfaceType();
245+
246+
// Strip off the curried self parameter if necessary.
247+
if (callee->hasCurriedSelf())
248+
fnInterfaceType = fnInterfaceType->castTo<AnyFunctionType>()->getResult();
249+
250+
if (auto *fn = fnInterfaceType->getAs<AnyFunctionType>()) {
251+
assert(fn->getNumParams() == fnType->getNumParams() &&
252+
"Parameter mismatch?");
253+
(void)fn;
254+
}
255+
} else {
256+
fnInterfaceType = resolveInterfaceType(rawFnType);
257+
}
258+
259+
auto argIdx = applyArgElt->getValue();
260+
auto paramIdx = applyArgElt->getValue2();
261+
262+
return FunctionArgApplyInfo(argExpr, argIdx, getType(argExpr), paramIdx,
263+
fnInterfaceType, fnType, callee);
264+
}
265+
113266
Type RequirementFailure::getOwnerType() const {
114267
auto *anchor = getRawAnchor();
115268

@@ -544,19 +697,8 @@ bool NoEscapeFuncToTypeConversionFailure::diagnoseParameterUse() const {
544697
return false;
545698

546699
auto *anchor = getAnchor();
547-
auto *locator = getLocator();
548700
auto diagnostic = diag::general_noescape_to_escaping;
549701

550-
auto getGenericParamType =
551-
[](TypeVariableType *typeVar) -> GenericTypeParamType * {
552-
auto *locator = typeVar->getImpl().getLocator();
553-
if (locator->isForGenericParameter()) {
554-
const auto &GP = locator->getPath().back();
555-
return GP.getGenericParameter();
556-
}
557-
return nullptr;
558-
};
559-
560702
ParamDecl *PD = nullptr;
561703
if (auto *DRE = dyn_cast<DeclRefExpr>(anchor)) {
562704
PD = dyn_cast<ParamDecl>(DRE->getDecl());
@@ -569,37 +711,29 @@ bool NoEscapeFuncToTypeConversionFailure::diagnoseParameterUse() const {
569711
// Let's check whether this is a function parameter passed
570712
// as an argument to another function which accepts @escaping
571713
// function at that position.
572-
auto path = locator->getPath();
573-
if (!path.empty() &&
574-
(path.back().getKind() == ConstraintLocator::ApplyArgToParam)) {
575-
if (auto paramType =
576-
getParameterTypeFor(getRawAnchor(), path.back().getValue2())) {
577-
if (paramType->isTypeVariableOrMember()) {
578-
auto diagnoseGenericParamFailure = [&](Type genericParam,
579-
GenericTypeParamDecl *decl) {
580-
emitDiagnostic(anchor->getLoc(),
581-
diag::converting_noespace_param_to_generic_type,
582-
PD->getName(), genericParam);
583-
584-
emitDiagnostic(decl, diag::generic_parameters_always_escaping);
585-
};
586-
587-
// If this is a situation when non-escaping parameter is passed
588-
// to the argument which represents generic parameter, there is
589-
// a tailored diagnostic for that.
590-
591-
if (auto *DMT = paramType->getAs<DependentMemberType>()) {
592-
auto baseTy = DMT->getBase()->castTo<TypeVariableType>();
593-
diagnoseGenericParamFailure(resolveType(DMT),
594-
getGenericParamType(baseTy)->getDecl());
595-
return true;
596-
}
714+
if (auto argApplyInfo = getFunctionArgApplyInfo(getLocator())) {
715+
auto paramInterfaceTy = argApplyInfo->getParamInterfaceType();
716+
if (paramInterfaceTy->isTypeParameter()) {
717+
auto diagnoseGenericParamFailure = [&](GenericTypeParamDecl *decl) {
718+
emitDiagnostic(anchor->getLoc(),
719+
diag::converting_noespace_param_to_generic_type,
720+
PD->getName(), paramInterfaceTy);
721+
722+
emitDiagnostic(decl, diag::generic_parameters_always_escaping);
723+
};
724+
725+
// If this is a situation when non-escaping parameter is passed
726+
// to the argument which represents generic parameter, there is
727+
// a tailored diagnostic for that.
728+
729+
if (auto *DMT = paramInterfaceTy->getAs<DependentMemberType>()) {
730+
diagnoseGenericParamFailure(DMT->getRootGenericParam()->getDecl());
731+
return true;
732+
}
597733

598-
auto *typeVar = paramType->getAs<TypeVariableType>();
599-
if (auto *GP = getGenericParamType(typeVar)) {
600-
diagnoseGenericParamFailure(GP, GP->getDecl());
601-
return true;
602-
}
734+
if (auto *GP = paramInterfaceTy->getAs<GenericTypeParamType>()) {
735+
diagnoseGenericParamFailure(GP->getDecl());
736+
return true;
603737
}
604738
}
605739

@@ -630,20 +764,6 @@ bool NoEscapeFuncToTypeConversionFailure::diagnoseParameterUse() const {
630764
return true;
631765
}
632766

633-
Type NoEscapeFuncToTypeConversionFailure::getParameterTypeFor(
634-
Expr *expr, unsigned paramIdx) const {
635-
auto choice = getChoiceFor(expr);
636-
if (!choice)
637-
return Type();
638-
639-
if (auto *fnType = choice->openedType->getAs<FunctionType>()) {
640-
const auto &param = fnType->getParams()[paramIdx];
641-
return param.getPlainType();
642-
}
643-
644-
return Type();
645-
}
646-
647767
bool MissingForcedDowncastFailure::diagnoseAsError() {
648768
if (hasComplexLocator())
649769
return false;
@@ -779,44 +899,6 @@ bool MemberAccessOnOptionalBaseFailure::diagnoseAsError() {
779899
resultIsOptional, SourceRange());
780900
}
781901

782-
Optional<AnyFunctionType::Param>
783-
MissingOptionalUnwrapFailure::getOperatorParameterFor(Expr *expr) const {
784-
auto *parentExpr = findParentExpr(expr);
785-
if (!parentExpr)
786-
return None;
787-
788-
auto getArgIdx = [](TupleExpr *tuple, Expr *argExpr) -> unsigned {
789-
for (unsigned i = 0, n = tuple->getNumElements(); i != n; ++i) {
790-
if (tuple->getElement(i) == argExpr)
791-
return i;
792-
}
793-
llvm_unreachable("argument is not in enclosing tuple?!");
794-
};
795-
796-
auto *tupleExpr = dyn_cast<TupleExpr>(parentExpr);
797-
if (!(tupleExpr && tupleExpr->isImplicit()))
798-
return None;
799-
800-
parentExpr = findParentExpr(tupleExpr);
801-
if (!(parentExpr && isa<ApplyExpr>(parentExpr)))
802-
return None;
803-
804-
auto &cs = getConstraintSystem();
805-
auto *fnExpr = cast<ApplyExpr>(parentExpr)->getFn();
806-
if (auto overload =
807-
getOverloadChoiceIfAvailable(cs.getConstraintLocator(fnExpr))) {
808-
if (auto *decl = overload->choice.getDecl()) {
809-
if (!decl->isOperator())
810-
return None;
811-
812-
auto *fnType = overload->openedType->castTo<FunctionType>();
813-
return fnType->getParams()[getArgIdx(tupleExpr, expr)];
814-
}
815-
}
816-
817-
return None;
818-
}
819-
820902
void MissingOptionalUnwrapFailure::offerDefaultValueUnwrapFixIt(
821903
DeclContext *DC, Expr *expr) const {
822904
auto *anchor = getAnchor();
@@ -827,10 +909,9 @@ void MissingOptionalUnwrapFailure::offerDefaultValueUnwrapFixIt(
827909
if (isa<InOutExpr>(anchor))
828910
return;
829911

830-
if (auto param = getOperatorParameterFor(anchor)) {
831-
if (param->isInOut())
912+
if (auto argApplyInfo = getFunctionArgApplyInfo(getLocator()))
913+
if (argApplyInfo->getParameterFlags().isInOut())
832914
return;
833-
}
834915

835916
auto diag = emitDiagnostic(expr->getLoc(), diag::unwrap_with_default_value);
836917

0 commit comments

Comments
 (0)