Skip to content

Commit 0b12c66

Browse files
committed
[ConstraintSystem] Fix missing arguments
While trying to match function types, detect and fix any missing arguments (by introducing type variables), such arguments would get type information from corresponding parameters and aid in producing solutions which are much easier to diagnose.
1 parent 12a65ff commit 0b12c66

File tree

4 files changed

+92
-2
lines changed

4 files changed

+92
-2
lines changed

include/swift/AST/Types.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2762,6 +2762,8 @@ class AnyFunctionType : public TypeBase {
27622762
bool operator!=(Param const &b) const { return !(*this == b); }
27632763

27642764
Param getWithoutLabel() const { return Param(Ty, Identifier(), Flags); }
2765+
2766+
Param withType(Type newType) const { return Param(newType, Label, Flags); }
27652767
};
27662768

27672769
class CanParam : public Param {

lib/Sema/CSFix.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,3 +342,16 @@ AllowInvalidInitRef::create(RefKind kind, ConstraintSystem &cs, Type baseTy,
342342
return new (cs.getAllocator()) AllowInvalidInitRef(
343343
cs, kind, baseTy, init, isStaticallyDerived, baseRange, locator);
344344
}
345+
346+
bool AddMissingArguments::diagnose(Expr *root, bool asNote) const {
347+
return false;
348+
}
349+
350+
AddMissingArguments *
351+
AddMissingArguments::create(ConstraintSystem &cs, FunctionType *funcType,
352+
llvm::ArrayRef<Param> synthesizedArgs,
353+
ConstraintLocator *locator) {
354+
unsigned size = totalSizeToAlloc<Param>(synthesizedArgs.size());
355+
void *mem = cs.getAllocator().Allocate(size, alignof(AddMissingArguments));
356+
return new (mem) AddMissingArguments(cs, funcType, synthesizedArgs, locator);
357+
}

lib/Sema/CSFix.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ enum class FixKind : uint8_t {
122122
/// derived (rather than an arbitrary value of metatype type) or the
123123
/// referenced constructor must be required.
124124
AllowInvalidInitRef,
125+
126+
/// If there are fewer arguments than parameters, let's fix that up
127+
/// by adding new arguments to the list represented as type variables.
128+
AddMissingArguments,
125129
};
126130

127131
class ConstraintFix {
@@ -614,6 +618,45 @@ class AllowInvalidInitRef final : public ConstraintFix {
614618
ConstraintLocator *locator);
615619
};
616620

621+
class AddMissingArguments final
622+
: public ConstraintFix,
623+
private llvm::TrailingObjects<AddMissingArguments,
624+
AnyFunctionType::Param> {
625+
friend TrailingObjects;
626+
627+
using Param = AnyFunctionType::Param;
628+
629+
FunctionType *Fn;
630+
unsigned NumSynthesized;
631+
632+
AddMissingArguments(ConstraintSystem &cs, FunctionType *funcType,
633+
llvm::ArrayRef<AnyFunctionType::Param> synthesizedArgs,
634+
ConstraintLocator *locator)
635+
: ConstraintFix(cs, FixKind::AddMissingArguments, locator), Fn(funcType),
636+
NumSynthesized(synthesizedArgs.size()) {
637+
std::uninitialized_copy(synthesizedArgs.begin(), synthesizedArgs.end(),
638+
getSynthesizedArgumentsBuf().begin());
639+
}
640+
641+
public:
642+
std::string getName() const override { return "synthesize missing argument(s)"; }
643+
644+
ArrayRef<Param> getSynthesizedArguments() const {
645+
return {getTrailingObjects<Param>(), NumSynthesized};
646+
}
647+
648+
bool diagnose(Expr *root, bool asNote = false) const override;
649+
650+
static AddMissingArguments *create(ConstraintSystem &cs, FunctionType *fnType,
651+
llvm::ArrayRef<Param> synthesizedArgs,
652+
ConstraintLocator *locator);
653+
654+
private:
655+
MutableArrayRef<Param> getSynthesizedArgumentsBuf() {
656+
return {getTrailingObjects<Param>(), NumSynthesized};
657+
}
658+
};
659+
617660
} // end namespace constraints
618661
} // end namespace swift
619662

lib/Sema/CSSimplify.cpp

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1250,8 +1250,39 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
12501250
auto argumentLocator = locator.withPathElement(
12511251
ConstraintLocator::FunctionArgument);
12521252

1253-
if (func1Params.size() != func2Params.size())
1254-
return getTypeMatchFailure(argumentLocator);
1253+
int diff = func1Params.size() - func2Params.size();
1254+
if (diff != 0) {
1255+
if (!shouldAttemptFixes())
1256+
return getTypeMatchFailure(argumentLocator);
1257+
1258+
auto *anchor = locator.trySimplifyToExpr();
1259+
if (!anchor)
1260+
return getTypeMatchFailure(argumentLocator);
1261+
1262+
// If there are missing arguments, let's add them
1263+
// using parameter as a template.
1264+
if (diff < 0) {
1265+
for (unsigned i = func1Params.size(),
1266+
n = func2Params.size(); i != n; ++i) {
1267+
auto *argLoc =
1268+
getConstraintLocator(anchor, LocatorPathElt::getTupleElement(i));
1269+
1270+
auto arg = func2Params[i].withType(createTypeVariable(argLoc));
1271+
func1Params.push_back(arg);
1272+
}
1273+
1274+
ArrayRef<AnyFunctionType::Param> argsRef(func1Params);
1275+
auto *fix = AddMissingArguments::create(*this, func2,
1276+
argsRef.take_back(abs(diff)),
1277+
getConstraintLocator(locator));
1278+
1279+
if (recordFix(fix))
1280+
return getTypeMatchFailure(argumentLocator);
1281+
} else {
1282+
// TODO(diagnostics): Add handling of extraneous arguments.
1283+
return getTypeMatchFailure(argumentLocator);
1284+
}
1285+
}
12551286

12561287
bool hasLabelingFailures = false;
12571288
for (unsigned i : indices(func1Params)) {
@@ -5628,6 +5659,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
56285659
case FixKind::AllowTypeOrInstanceMember:
56295660
case FixKind::AllowInvalidPartialApplication:
56305661
case FixKind::AllowInvalidInitRef:
5662+
case FixKind::AddMissingArguments:
56315663
llvm_unreachable("handled elsewhere");
56325664
}
56335665

0 commit comments

Comments
 (0)