Skip to content

[Sema] Type inference from default expression doesn’t compile in free function #79989

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
May 29, 2025
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}}