Skip to content
21 changes: 15 additions & 6 deletions lib/Sema/TypeCheckConstraints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -639,17 +639,26 @@ Type TypeChecker::typeCheckParameterDefault(Expr *&defaultValue,
// Check whether generic parameters are only mentioned once in
// the anchor's signature.
{
auto anchorTy = anchor->getInterfaceType()->castTo<GenericFunctionType>();
auto anchorTy = anchor->getInterfaceType()->castTo<AnyFunctionType>();

// Reject if generic parameters are used in multiple different positions
// in the parameter list.
if (anchor->hasCurriedSelf())
anchorTy = anchorTy->getResult()->castTo<AnyFunctionType>();

SmallPtrSet<CanType, 4> inferrableParams;
collectReferencedGenericParams(paramInterfaceTy, inferrableParams);

llvm::SmallVector<unsigned, 2> affectedParams;
for (unsigned i : indices(anchorTy->getParams())) {
const auto &param = anchorTy->getParams()[i];

if (containsTypes(param.getPlainType()))
affectedParams.push_back(i);
SmallPtrSet<CanType, 4> referencedGenericParams;
collectReferencedGenericParams(anchorTy->getParams()[i].getPlainType(), referencedGenericParams);

for (auto can : referencedGenericParams) {
if (can->is<GenericTypeParamType>() && inferrableParams.contains(can)) {
affectedParams.push_back(i);
break;
}
}
}

if (affectedParams.size() > 1) {
Expand Down
69 changes: 33 additions & 36 deletions lib/Sema/TypeCheckGeneric.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,31 +313,15 @@ void TypeChecker::checkProtocolSelfRequirements(ValueDecl *decl) {
}
}

/// All generic parameters of a generic function must be referenced in the
/// declaration's type, otherwise we have no way to infer them.
void TypeChecker::checkReferencedGenericParams(GenericContext *dc) {
// Don't do this check for accessors: they're not used directly, so we
// never need to infer their generic arguments. This is mostly a
// compile-time optimization, but it also avoids problems with accessors
// like 'read' and 'modify' that would arise due to yields not being
// part of the formal type.
if (isa<AccessorDecl>(dc))
return;

auto *genericParams = dc->getGenericParams();
auto genericSig = dc->getGenericSignatureOfContext();
if (!genericParams)
return;

auto *decl = cast<ValueDecl>(dc->getInnermostDeclarationDeclContext());
void TypeChecker::collectReferencedGenericParams(Type ty, SmallPtrSet<CanType, 4> &referenced) {

// A helper class to collect referenced generic type parameters
// and dependent member types.
class ReferencedGenericTypeWalker : public TypeWalker {
SmallPtrSet<CanType, 4> ReferencedGenericParams;
SmallPtrSet<CanType, 4> &ReferencedGenericParams;

public:
ReferencedGenericTypeWalker() {}
ReferencedGenericTypeWalker(SmallPtrSet<CanType, 4> &referenced) : ReferencedGenericParams(referenced) {}
Action walkToTypePre(Type ty) override {
// Find generic parameters or dependent member types.
// Once such a type is found, don't recurse into its children.
Expand Down Expand Up @@ -368,24 +352,39 @@ void TypeChecker::checkReferencedGenericParams(GenericContext *dc) {

return Action::Continue;
}

SmallPtrSetImpl<CanType> &getReferencedGenericParams() {
return ReferencedGenericParams;
}
};

ty.walk(ReferencedGenericTypeWalker(referenced));
}

/// All generic parameters of a generic function must be referenced in the
/// declaration's type, otherwise we have no way to infer them.
void TypeChecker::checkReferencedGenericParams(GenericContext *dc) {
// Don't do this check for accessors: they're not used directly, so we
// never need to infer their generic arguments. This is mostly a
// compile-time optimization, but it also avoids problems with accessors
// like 'read' and 'modify' that would arise due to yields not being
// part of the formal type.
if (isa<AccessorDecl>(dc))
return;

auto *genericParams = dc->getGenericParams();
auto genericSig = dc->getGenericSignatureOfContext();
if (!genericParams)
return;

auto *decl = cast<ValueDecl>(dc->getInnermostDeclarationDeclContext());

// Set of generic params referenced in parameter types,
// return type or requirements.
SmallPtrSet<CanType, 4> referencedGenericParams;

// Collect all generic params referenced in parameter types and
// return type.
ReferencedGenericTypeWalker paramsAndResultWalker;
auto *funcTy = decl->getInterfaceType()->castTo<GenericFunctionType>();
for (const auto &param : funcTy->getParams())
param.getPlainType().walk(paramsAndResultWalker);
funcTy->getResult().walk(paramsAndResultWalker);

// Set of generic params referenced in parameter types,
// return type or requirements.
auto &referencedGenericParams =
paramsAndResultWalker.getReferencedGenericParams();
collectReferencedGenericParams(param.getPlainType(), referencedGenericParams);
collectReferencedGenericParams(funcTy->getResult(), referencedGenericParams);

// Check if at least one of the generic params in the requirement refers
// to an already referenced generic parameter. If this is the case,
Expand All @@ -409,13 +408,11 @@ void TypeChecker::checkReferencedGenericParams(GenericContext *dc) {
}

// Collect generic parameter types referenced by types used in a requirement.
ReferencedGenericTypeWalker walker;
SmallPtrSet<CanType, 4> genericParamsUsedByRequirementTypes;
if (first && first->hasTypeParameter())
first.walk(walker);
collectReferencedGenericParams(first, genericParamsUsedByRequirementTypes);
if (second && second->hasTypeParameter())
second.walk(walker);
auto &genericParamsUsedByRequirementTypes =
walker.getReferencedGenericParams();
collectReferencedGenericParams(second, genericParamsUsedByRequirementTypes);

// If at least one of the collected generic types or a root generic
// parameter of dependent member types is known to be referenced by
Expand Down
2 changes: 2 additions & 0 deletions lib/Sema/TypeChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,8 @@ std::optional<BraceStmt *> applyResultBuilderBodyTransform(FuncDecl *func,

bool typeCheckTapBody(TapExpr *expr, DeclContext *DC);

void collectReferencedGenericParams(Type ty, SmallPtrSet<CanType, 4> &referenced);

Type typeCheckParameterDefault(Expr *&defaultValue, DeclContext *DC,
Type paramType, bool isAutoClosure,
bool atCallerSide);
Expand Down
2 changes: 1 addition & 1 deletion test/Concurrency/sendable_keypaths.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class NonSendable : Hashable {
final class CondSendable<T> : Hashable {
init(_: T) {}
init(_: Int) {}
init(_: T, other: T = 42) {}
init<U>(_: T, other: U = 42) {}
init<Q>(_: [Q] = []) {}

static func == (x: CondSendable, y: CondSendable) -> Bool { false }
Expand Down
21 changes: 21 additions & 0 deletions test/Constraints/type_inference_from_default_exprs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,11 @@ func main() {
takesCircle(.init()) // Ok
takesRectangle(.init())
// expected-error@-1 {{cannot convert default value of type 'Rectangle' to expected argument type 'Circle' for parameter #0}}

testS72199_2(x: 0)
testS72199_3(xs: 0, 0)
testS72199_4(x: 0)
testS72199_5(x: 0)
}

func test_magic_defaults() {
Expand Down Expand Up @@ -269,3 +274,19 @@ do {

func testInferenceFromClosureVarInvalid<T>(x: T = { let x = "" as Int; return x }()) {}
// expected-error@-1 {{cannot convert value of type 'String' to type 'Int' in coercion}}

// https://github.com/swiftlang/swift/issues/72199
enum S72199_1 {
func testS72199_1<T>(_: T = 42, _: [T]) {}
// expected-error@-1 {{cannot use default expression for inference of 'T' because it is inferrable from parameters #0, #1}}
}

func testS72199_2<T: P>(x: T.X, y: T = S()) { } // Ok
func testS72199_3<each T: P>(xs: repeat (each T).X, ys: (repeat each T) = (S(), S())) {} // Ok

typealias S72199_4<T> = Int
func testS72199_4<T>(x: S72199_4<T>, y: T = "") {} // Ok

func testS72199_5<T: P>(x: T.X, y: (T, T.X) = (S(), 0)) {} // Ok
func testS72199_6<T: P>(x: T, y: (T, T.X) = (S(), 0)) {}
// expected-error@-1 {{cannot use default expression for inference of '(T, T.X)' because it is inferrable from parameters #0, #1}}