//===--- ConstraintSystem.cpp - Constraint-based Type Checking ------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2026 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file implements various subclasses of BindingProducer to generate // successive choices in a binding set, disjunction, or conjunction. // //===----------------------------------------------------------------------===// #include "swift/Sema/BindingProducer.h" #include "swift/Sema/Constraint.h" #include "swift/Sema/ConstraintSystem.h" #include "swift/Sema/TypeVariableType.h" using namespace swift; using namespace constraints; // Given a possibly-Optional type, return the direct superclass of the // (underlying) type wrapped in the same number of optional levels as // type. static Type getOptionalSuperclass(Type type) { int optionalLevels = 0; while (auto underlying = type->getOptionalObjectType()) { ++optionalLevels; type = underlying; } Type superclass; if (auto *existential = type->getAs()) { auto constraintTy = existential->getConstraintType(); if (auto *compositionTy = constraintTy->getAs()) { SmallVector members; bool found = false; // Preserve all of the protocol requirements of the type i.e. // if the type was `any B & P` where `B : A` the supertype is // going to be `any A & P`. // // This is especially important for Sendable key paths because // to reserve sendability of the original type. for (auto member : compositionTy->getMembers()) { if (member->getClassOrBoundGenericClass()) { member = member->getSuperclass(); if (!member) return Type(); found = true; } members.push_back(member); } if (!found) return Type(); superclass = ExistentialType::get( ProtocolCompositionType::get(type->getASTContext(), members, compositionTy->getInverses(), compositionTy->hasExplicitAnyObject())); } else { // Avoid producing superclass for situations like `any P` where `P` is // `protocol P : C`. return Type(); } } else { superclass = type->getSuperclass(); } if (!superclass) return Type(); while (optionalLevels--) superclass = OptionalType::get(superclass); return superclass; } /// Enumerates all of the 'direct' supertypes of the given type. /// /// The direct supertype S of a type T is a supertype of T (e.g., T < S) /// such that there is no type U where T < U and U < S. static SmallVector enumerateDirectSupertypes(Type type) { SmallVector result; if (type->is() || type->is()) { type = type->getWithoutSpecifierType(); result.push_back(type); } if (auto superclass = getOptionalSuperclass(type)) { // FIXME: Can also weaken to the set of protocol constraints, but only // if there are any protocols that the type conforms to but the superclass // does not. result.push_back(superclass); } // FIXME: lots of other cases to consider! return result; } TypeVarBindingProducer::TypeVarBindingProducer( ConstraintSystem &cs, TypeVariableType *typeVar, const BindingSet &bindings) : BindingProducer(cs, typeVar->getImpl().getLocator()), TypeVar(typeVar), CanBeNil(bindings.canBeNil()) { if (bindings.isDirectHole()) { auto *locator = getLocator(); // If this type variable is associated with a code completion token // and it failed to infer any bindings let's adjust holes's locator // to point to a code completion token to avoid attempting to "fix" // this problem since its rooted in the fact that constraint system // is under-constrained. if (bindings.getAssociatedCodeCompletionToken()) { locator = CS.getConstraintLocator(bindings.getAssociatedCodeCompletionToken()); } Bindings.push_back(Binding::forHole(TypeVar, locator)); return; } // A binding to `Any` which should always be considered as a last resort. std::optional Any; auto addBinding = [&](const Binding &binding) { // Adjust optionality of existing bindings based on presence of // `ExpressibleByNilLiteral` requirement. if (requiresOptionalAdjustment(binding)) { Bindings.push_back( binding.withType(OptionalType::get(binding.BindingType))); } else if (binding.BindingType->isAny()) { Any.emplace(binding); } else { Bindings.push_back(binding); } }; if (TypeVar->getImpl().isPackExpansion()) { SmallVector viableBindings; // Collect possible contextual types (keep in mind that pack // expansion type variable gets bound to its "opened" type // regardless). To be viable the binding has to come from `bind` // or `equal` constraint (i.e. same-type constraint or explicit // generic argument) and be fully resolved. llvm::copy_if(bindings.Bindings, std::back_inserter(viableBindings), [&](const Binding &binding) { auto *source = binding.getSource(); if (source->getKind() == ConstraintKind::Bind || source->getKind() == ConstraintKind::Equal) { auto type = binding.BindingType; return type->is() && !type->hasTypeVariable(); } return false; }); // If there is a single fully resolved contextual type, let's // use it as a binding to help with performance and diagnostics. if (viableBindings.size() == 1) { addBinding(viableBindings.front()); } else { for (auto *constraint : bindings.Defaults) { Bindings.push_back(getDefaultBinding(constraint)); } } return; } for (const auto &binding : bindings.Bindings) { addBinding(binding); } // Infer defaults based on "uncovered" literal protocol requirements. for (const auto &literal : bindings.Literals) { if (!literal.viableAsBinding()) continue; // We need to figure out whether this is a direct conformance // requirement or inferred transitive one to identify binding // kind correctly. addBinding({literal.getDefaultType(), literal.isDirectRequirement() ? BindingKind::Subtypes : BindingKind::Supertypes, literal.getSource()}); } // Let's always consider `Any` to be a last resort binding because // it's always better to infer concrete type and erase it if required // by the context. if (Any) { Bindings.push_back(*Any); } { bool noBindings = Bindings.empty(); for (auto *constraint : bindings.Defaults) { if (noBindings) { // If there are no direct or transitive bindings to attempt // let's add defaults to the list right away. Bindings.push_back(getDefaultBinding(constraint)); } else { // Otherwise let's delay attempting default bindings // until all of the direct & transitive bindings and // their derivatives have been attempted. DelayedDefaults.push_back(constraint); } } } } bool TypeVarBindingProducer::requiresOptionalAdjustment( const Binding &binding) const { // If type variable can't be `nil` then adjustment is // not required. if (!CanBeNil) return false; if (binding.Kind == BindingKind::Supertypes) { auto type = binding.BindingType->getRValueType(); // If the type doesn't conform to ExpressibleByNilLiteral, // produce an optional of that type as a potential binding. We // overwrite the binding in place because the non-optional type // will fail to type-check against the nil-literal conformance. auto *proto = CS.getASTContext().getProtocol( KnownProtocolKind::ExpressibleByNilLiteral); return !CS.lookupConformance(type, proto); } else if (binding.isDefaultableBinding() && binding.BindingType->isAny()) { return true; } return false; } PotentialBinding TypeVarBindingProducer::getDefaultBinding(Constraint *constraint) const { assert(constraint->getKind() == ConstraintKind::Defaultable || constraint->getKind() == ConstraintKind::FallbackType); auto type = constraint->getSecondType(); Binding binding{type, BindingKind::Exact, constraint}; return requiresOptionalAdjustment(binding) ? binding.withType(OptionalType::get(type)) : binding; } bool TypeVarBindingProducer::computeNext() { SmallVector newBindings; auto addNewBinding = [&](Binding binding) { auto type = binding.BindingType; // If we've already tried this binding, move on. if (!BoundTypes.insert(type.getPointer()).second) return; if (!ExploredTypes.insert(type->getCanonicalType()).second) return; newBindings.push_back(std::move(binding)); }; // Let's attempt only directly inferrable bindings for // a type variable representing a closure type because // such type variables are handled specially and only // bound to a type inferred from their expression, having // contextual bindings is just a trigger for that to // happen. if (TypeVar->getImpl().isClosureType()) return false; for (auto &binding : Bindings) { const auto type = binding.BindingType; assert(!type->hasError()); // If we have a protocol with a default type, look for alternative // types to the default. if (NumTries == 0 && binding.hasDefaultedLiteralProtocol()) { auto knownKind = *(binding.getDefaultedLiteralProtocol()->getKnownProtocolKind()); SmallVector scratch; for (auto altType : CS.getAlternativeLiteralTypes(knownKind, scratch)) { addNewBinding(binding.withSameSource(altType, BindingKind::Subtypes)); } } if (getLocator()->directlyAt() && TypeVar->getImpl().canBindToLValue() && !binding.BindingType->is()) { // Result of force unwrap is always connected to its base // optional type via `OptionalObject` constraint which // preserves l-valueness, so in case where object type got // inferred before optional type (because it got the // type from context e.g. parameter type of a function call), // we need to test type with and without l-value after // delaying bindings for as long as possible. addNewBinding(binding.withType(LValueType::get(binding.BindingType))); } // There is a tailored fix for optional key path root references, // let's not create ambiguity by attempting unwrap when it's // not allowed. if (binding.Kind != BindingKind::Subtypes && getLocator()->isKeyPathRoot() && type->getOptionalObjectType()) continue; // Allow solving for T even for a binding kind where that's invalid // if fixes are allowed, because that gives us the opportunity to // match T? values to the T binding by adding an unwrap fix. if (binding.Kind == BindingKind::Subtypes || CS.shouldAttemptFixes()) { // If we were unsuccessful solving for T?, try solving for T. if (auto objTy = type->getOptionalObjectType()) { // TODO: This could be generalized in the future to cover all patterns // that have an intermediate type variable in subtype/conversion chain. // // Let's not perform $T? -> $T for closure result types to avoid having // to re-discover solutions that differ only in location of optional // injection. `Void` is a special case because in $T_result position // it has special semantics and enables T? -> Void conversions. // // The pattern with such type variables is: // // $T_body $T_result $T_contextual_result // // When $T_contextual_result is Optional<$U>, the optional injection // can either happen from $T_body or from $T_result (if `return` // expression is non-optional), if we allow both the solver would // find two solutions that differ only in location of optional // injection. if (!TypeVar->getImpl().isClosureResultType() || objTy->isVoid() || objTy->isTypeVariableOrMember()) { // If T is a type variable, only attempt this if both the // type variable we are trying bindings for, and the type // variable we will attempt to bind, both have the same // polarity with respect to being able to bind lvalues. if (auto otherTypeVar = objTy->getAs()) { if (TypeVar->getImpl().canBindToLValue() == otherTypeVar->getImpl().canBindToLValue()) { addNewBinding(binding.withType(objTy)); } } else { addNewBinding(binding.withType(objTy)); } } } } auto srcLocator = binding.getLocator(); if (srcLocator && (srcLocator->isLastElement() || srcLocator->isLastElement()) && !type->hasTypeVariable() && type->isKnownStdlibCollectionType()) { // If the type binding comes from the argument conversion, let's // instead of binding collection types directly, try to bind // using temporary type variables substituted for element // types, that's going to ensure that subtype relationship is // always preserved. auto *BGT = type->castTo(); auto dstLocator = TypeVar->getImpl().getLocator(); auto newType = CS.openUnboundGenericType(BGT->getDecl(), BGT->getParent(), dstLocator, /*isTypeResolution=*/false) ->reconstituteSugar(/*recursive=*/false); addNewBinding(binding.withType(newType)); } if (binding.Kind == BindingKind::Supertypes) { // If this is a type variable representing closure result, // which is on the right-side of some relational constraint // let's have it try `Void` as well because there is an // implicit conversion `() -> T` to `() -> Void` and this // helps to avoid creating a thunk to support it. // Avoid doing this is we already have a hole binding since // introducing Void will just cause local solution ambiguities. if (getLocator()->isLastElement() && binding.Kind == AllowedBindingKind::Supertypes && !binding.BindingType->isPlaceholder()) { auto voidType = CS.getASTContext().TheEmptyTupleType; addNewBinding(binding.withSameSource(voidType, BindingKind::Exact)); } for (auto supertype : enumerateDirectSupertypes(type)) { // If we're not allowed to try this binding, skip it. if (inference::checkTypeOfBinding(TypeVar, supertype)) { // A key path type cannot be bound to type-erased key path variants. if (TypeVar->getImpl().isKeyPathType() && isTypeErasedKeyPathType(supertype)) continue; addNewBinding(binding.withType(supertype)); } } } } if (newBindings.empty()) { // If key path type had contextual types, let's not attempt fallback. if (TypeVar->getImpl().isKeyPathType() && !ExploredTypes.empty()) return false; // Add defaultable constraints (if any). for (auto *constraint : DelayedDefaults) { if (constraint->getKind() == ConstraintKind::FallbackType) { // If there are no other possible bindings for this variable // let's default it to the fallback type, otherwise we should // only attempt contextual types. if (!ExploredTypes.empty()) continue; } addNewBinding(getDefaultBinding(constraint)); } // Drop all of the default since we have converted them into bindings. DelayedDefaults.clear(); } if (newBindings.empty()) return false; Index = 0; ++NumTries; Bindings = std::move(newBindings); return true; } std::optional> TypeVariableBinding::fixForHole(ConstraintSystem &cs) const { auto *dstLocator = TypeVar->getImpl().getLocator(); auto *srcLocator = Binding.getLocator(); // FIXME: This check could be turned into an assert once // all code completion kinds are ported to use // `TypeChecker::typeCheckForCodeCompletion` API. if (cs.isForCodeCompletion()) { // If the hole is originated from code completion expression // let's not try to fix this, anything connected to a // code completion is allowed to be a hole because presence // of a code completion token makes constraint system // under-constrained due to e.g. lack of expressions on the // right-hand side of the token, which are required for a // regular type-check. if (dstLocator->directlyAt() || srcLocator->directlyAt()) return std::nullopt; } unsigned defaultImpact = 1; if (auto *GP = TypeVar->getImpl().getGenericParameter()) { // If it is representative for a key path root, let's emit a more // specific diagnostic. auto *keyPathRoot = cs.isRepresentativeFor(TypeVar, ConstraintLocator::KeyPathRoot); if (keyPathRoot) { ConstraintFix *fix = SpecifyKeyPathRootType::create( cs, keyPathRoot->getImpl().getLocator()); return std::make_pair(fix, defaultImpact); } else { auto path = dstLocator->getPath(); // Drop `generic parameter` locator element so that all missing // generic parameters related to the same path can be coalesced later. ConstraintFix *fix = DefaultGenericArgument::create( cs, GP, cs.getConstraintLocator(dstLocator->getAnchor(), path.drop_back())); return std::make_pair(fix, defaultImpact); } } if (TypeVar->getImpl().isClosureParameterType()) { ConstraintFix *fix = SpecifyClosureParameterType::create(cs, dstLocator); return std::make_pair(fix, defaultImpact); } if (TypeVar->getImpl().isClosureResultType()) { auto *closure = castToExpr(dstLocator->getAnchor()); // If the whole body is being ignored due to a pre-check failure, // let's not record a fix about result type since there is // just not enough context to infer it without a body. auto *closureLoc = cs.getConstraintLocator(closure); if (cs.hasFixFor(closureLoc, FixKind::IgnoreInvalidResultBuilderBody) || cs.hasFixFor(closureLoc, FixKind::IgnoreResultBuilderWithReturnStmts)) return std::nullopt; ConstraintFix *fix = SpecifyClosureReturnType::create(cs, dstLocator); return std::make_pair(fix, defaultImpact); } if (srcLocator->directlyAt()) { ConstraintFix *fix = SpecifyObjectLiteralTypeImport::create(cs, dstLocator); return std::make_pair(fix, defaultImpact); } if (srcLocator->isKeyPathRoot()) { // If we recorded an invalid key path fix, let's skip this specify root // type fix because it wouldn't produce a useful diagnostic. auto *kpLocator = cs.getConstraintLocator(srcLocator->getAnchor()); if (cs.hasFixFor(kpLocator, FixKind::AllowKeyPathWithoutComponents)) return std::nullopt; // If key path has any invalid component, let's just skip fix because the // invalid component would be already diagnosed. auto keyPath = castToExpr(srcLocator->getAnchor()); if (llvm::any_of(keyPath->getComponents(), [](KeyPathExpr::Component component) { return !component.isValid(); })) return std::nullopt; ConstraintFix *fix = SpecifyKeyPathRootType::create(cs, dstLocator); return std::make_pair(fix, defaultImpact); } if (srcLocator->isLastElement()) { // When a 'nil' has a placeholder as contextual type there is not enough // information to resolve it, so let's record a specify contextual type for // nil fix. if (isExpr(srcLocator->getAnchor())) { ConstraintFix *fix = SpecifyContextualTypeForNil::create(cs, dstLocator); return std::make_pair(fix, /*impact=*/(unsigned)10); } // If the placeholder is in an invalid position, we'll have already // recorded a fix, and can skip recording another. if (cs.hasFixFor(dstLocator, FixKind::IgnoreInvalidPlaceholder)) return std::nullopt; ConstraintFix *fix = SpecifyTypeForPlaceholder::create(cs, srcLocator); return std::make_pair(fix, defaultImpact); } if (dstLocator->directlyAt()) { // This is a dramatic event, it means that there is absolutely // no contextual information to resolve type of `nil`. ConstraintFix *fix = SpecifyContextualTypeForNil::create(cs, dstLocator); return std::make_pair(fix, /*impact=*/(unsigned)10); } if (auto pattern = dstLocator->getPatternMatch()) { if (dstLocator->isLastElement()) { // If this is the pattern in a for loop, and we have a mismatch of the // element type, then we don't have any useful contextual information // for the pattern, and can just bind to a hole without needing to penalize // the solution further. auto *seqLoc = cs.getConstraintLocator( dstLocator->getAnchor(), ConstraintLocator::SequenceElementType); if (cs.hasFixFor(seqLoc, FixKind::IgnoreCollectionElementContextualMismatch)) { return std::nullopt; } if (dstLocator->getAnchor().isExpr(ExprKind::CodeCompletion)) { // Ignore the hole if it is because the right-hand-side of the pattern // match is a code completion token. Assigning a high fix score to this // mismatch won't help. In fact, it can harm because we might have a // different exploration path in the constraint system that gives up // earlier (eg. because code completion is in a closure that doesn't // match the expected parameter of a function call) and might thus get a // better score, despite not having any information about the code // completion token at all. return std::nullopt; } // Not being able to infer the type of a variable in a pattern binding // decl is more dramatic than anything that could happen inside the // expression because we want to preferrably point the diagnostic to a // part of the expression that caused us to be unable to infer the // variable's type. ConstraintFix *fix = IgnoreUnresolvedPatternVar::create(cs, pattern.get(), dstLocator); return std::make_pair(fix, /*impact=*/(unsigned)100); } } if (srcLocator->isLastElement()) { auto *baseExpr = castToExpr(srcLocator->getAnchor()); ConstraintFix *fix = SpecifyBaseTypeForContextualMember::create( cs, baseExpr->getName(), srcLocator); return std::make_pair(fix, defaultImpact); } if (dstLocator->isLastElement()) { // A hole appears as an element of generic pack params ConstraintFix *Fix = SpecifyPackElementType::create(cs, dstLocator); return std::make_pair(Fix, defaultImpact); } return std::nullopt; } static bool shouldIgnoreHoleForCodeCompletion(ConstraintSystem &cs, TypeVariableType *typeVar, ConstraintLocator *srcLocator) { if (!cs.isForCodeCompletion()) return false; // Don't penalize solutions with unresolved generics. if (typeVar->getImpl().getGenericParameter()) return true; // Don't penalize solutions if we couldn't determine the type of the code // completion token. We still want to examine the surrounding types in // that case. if (typeVar->getImpl().isCodeCompletionToken()) return true; // When doing completion in a result builder, we avoid solving unrelated // expressions by replacing them with unbound placeholder variables. // As such, we need to avoid penalizing holes for references to // placeholder variables. if (srcLocator->isLastElement()) { if (auto *DRE = getAsExpr(srcLocator->getAnchor())) { if (auto *VD = dyn_cast_or_null(DRE->getDecl())) { if (auto *PBD = VD->getParentPatternBinding()) { if (isPlaceholderVar(PBD)) return true; } } } } // Don't penalize solutions with holes due to missing arguments after the // code completion position. auto argLoc = srcLocator->findLast(); if (argLoc && argLoc->isAfterCodeCompletionLoc()) return true; // Don't penalize solutions that have holes for ignored arguments. if (cs.hasArgumentsIgnoredForCodeCompletion()) { // Avoid simplifying the locator if the constraint system didn't ignore // any arguments. auto argExpr = simplifyLocatorToAnchor(typeVar->getImpl().getLocator()); if (cs.isArgumentIgnoredForCodeCompletion(argExpr.dyn_cast())) { return true; } } return false; } bool TypeVariableBinding::attempt(ConstraintSystem &cs) const { auto type = Binding.BindingType; auto *srcLocator = Binding.getLocator(); auto *dstLocator = TypeVar->getImpl().getLocator(); if (Binding.hasDefaultedLiteralProtocol()) { type = cs.replaceInferableTypesWithTypeVars(type, dstLocator); type = type->reconstituteSugar(/*recursive=*/false); } // If type variable has been marked as a possible hole due to // e.g. reference to a missing member. Let's propagate that // information to the object type of the optional type it's // about to be bound to. // // In some situations like pattern bindings e.g. `if let x = base?.member` // - if `member` doesn't exist, `x` cannot be determined either, which // leaves `OptionalEvaluationExpr` representing outer type of `base?.member` // without any contextual information, so even though `x` would get // bound to result type of the chain, underlying type variable wouldn't // be resolved, so we need to propagate holes up the conversion chain. // Also propagate in code completion mode because in some cases code // completion relies on type variable being a potential hole. if (TypeVar->getImpl().canBindToHole()) { if (srcLocator->directlyAt() || cs.isForCodeCompletion()) { if (auto objectTy = type->getOptionalObjectType()) { if (auto *typeVar = objectTy->getAs()) { cs.recordPotentialHole(typeVar); } } } } ConstraintSystem::TypeMatchOptions options; options |= ConstraintSystem::TMF_GenerateConstraints; options |= ConstraintSystem::TMF_BindingTypeVariable; auto result = cs.matchTypes(TypeVar, type, ConstraintKind::Bind, options, srcLocator); if (result.isFailure()) { if (cs.isDebugMode()) { PrintOptions PO = PrintOptions::forDebugging(); llvm::errs().indent(cs.solverState->getCurrentIndent()) << "(failed to establish binding " << TypeVar->getString(PO) << " := " << type->getString(PO) << ")\n"; } return false; } auto reportHole = [&]() { if (shouldIgnoreHoleForCodeCompletion(cs, TypeVar, srcLocator)) return false; // Reflect in the score that this type variable couldn't be // resolved and had to be bound to a placeholder "hole" type. cs.increaseScore(SK_Hole, srcLocator); if (auto fix = fixForHole(cs)) { if (cs.recordFix(/*fix=*/fix->first, /*impact=*/fix->second)) return true; } return false; }; // If this was from a defaultable binding note that. if (Binding.isDefaultableBinding()) { cs.recordDefaultedConstraint(srcLocator); // Fail if hole reporting fails. if (type->isPlaceholder() && reportHole()) return false; } if (cs.simplify()) return false; // If all of the re-activated constraints where simplified, // let's notify binding inference about the fact that type // variable has been bound successfully. cs.getConstraintGraph().introduceToInference(TypeVar, type); return true; } namespace { /// Find any type variable references inside of an AST node. class TypeVariableRefFinder : public ASTWalker { /// A stack of all closures the walker encountered so far. SmallVector ClosureDCs; ConstraintSystem &CS; ASTNode Parent; llvm::SmallPtrSetImpl &ReferencedVars; public: TypeVariableRefFinder( ConstraintSystem &cs, ASTNode parent, ContextualTypeInfo context, llvm::SmallPtrSetImpl &referencedVars) : CS(cs), Parent(parent), ReferencedVars(referencedVars) { if (auto ty = context.getType()) inferVariables(ty); if (auto *closure = getAsExpr(Parent)) ClosureDCs.push_back(closure); } MacroWalking getMacroWalkingBehavior() const override { return MacroWalking::Arguments; } PreWalkResult walkToExprPre(Expr *expr) override { if (auto *closure = dyn_cast(expr)) { ClosureDCs.push_back(closure); } if (auto *joinExpr = dyn_cast(expr)) { // If this join is over a known type, let's // analyze it too because it can contain type // variables. if (!joinExpr->getVar()) inferVariables(joinExpr->getType()); } if (auto *DRE = dyn_cast(expr)) { auto *decl = DRE->getDecl(); if (auto type = CS.getTypeIfAvailable(decl)) { auto &ctx = CS.getASTContext(); // If this is not one of the closure parameters which // is inferrable from the body, let's replace type // variables with errors to avoid bringing external // information to the element component. if (type->hasTypeVariable() && !(isa(decl) || decl->getName() == ctx.Id_builderSelf)) { // If there are type variables left in the simplified version, // it means that this is an invalid external declaration // relative to this element's context. if (CS.simplifyType(type)->hasTypeVariable()) { auto transformedTy = type.transformRec([&](Type type) -> std::optional { if (type->is()) { return Type(ErrorType::get(CS.getASTContext())); } return std::nullopt; }); CS.setType(decl, transformedTy); return Action::Continue(expr); } } inferVariables(type); return Action::Continue(expr); } auto var = dyn_cast(decl); if (!var) return Action::Continue(expr); // If there is no type recorded yet, let's check whether // it is a placeholder variable implicitly generated by the // compiler. if (auto *PB = var->getParentPatternBinding()) { if (auto placeholderTy = isPlaceholderVar(PB)) { auto openedTy = CS.replaceInferableTypesWithTypeVars( placeholderTy, CS.getConstraintLocator(expr)); inferVariables(openedTy); CS.setType(var, openedTy); } } } // If closure appears inside of a pack expansion, the elements // that reference pack elements have to bring expansion's shape // type in scope to make sure that the shapes match. if (auto *packElement = getAsExpr(expr)) { if (auto *outerExpansion = CS.getPackElementExpansion(packElement)) { auto *expansionTy = CS.simplifyType(CS.getType(outerExpansion)) ->castTo(); expansionTy->getCountType()->getTypeVariables(ReferencedVars); } } return Action::Continue(expr); } PostWalkResult walkToExprPost(Expr *expr) override { if (isa(expr)) { ClosureDCs.pop_back(); } return Action::Continue(expr); } PreWalkResult walkToStmtPre(Stmt *stmt) override { // Return statements have to reference outside result type // since all of them are joined by it if it's not specified // explicitly. if (isa(stmt)) { if (auto *closure = getAsExpr(Parent)) { // Return is only viable if it belongs to a parent closure. if (currentClosureDC() == closure) inferVariables(CS.getClosureType(closure)->getResult()); } } return Action::Continue(stmt); } PreWalkAction walkToDeclPre(Decl *D) override { /// Decls get type-checked separately, except for PatternBindingDecls, /// whose initializers we want to walk into. return Action::VisitNodeIf(isa(D)); } private: DeclContext *currentClosureDC() const { return ClosureDCs.empty() ? nullptr : ClosureDCs.back(); } void inferVariables(Type type) { type = type->getWithoutSpecifierType(); // Record the type variable itself because it has to // be in scope even when already bound. if (auto *typeVar = type->getAs()) { ReferencedVars.insert(typeVar); // It is possible that contextual type of a parameter/result // has been assigned to e.g. an anonymous or named argument // early, to facilitate closure type checking. Such a // type can have type variables inside e.g. // // func test(_: (UnsafePointer) -> Void) {} // // test { ptr in // ... // } // // Type variable representing `ptr` in the body of // this closure would be bound to `UnsafePointer<$T>` // in this case, where `$T` is a type variable for a // generic parameter `T`. type = CS.getFixedTypeRecursive(typeVar, /*wantRValue=*/false); if (type->isEqual(typeVar)) return; } // Desugar type before collecting type variables, otherwise // we can bring in scope unrelated type variables passed // into the closure (via parameter/result) from contextual type. // For example `Typealias<$T, $U>.Context` which desugars into // `_Context<$U>` would bring in `$T` that could be inferrable // only after the body of the closure is solved. type = type->getCanonicalType(); // Don't walk into the opaque archetypes because they are not // transparent in this context - `some P` could reference a // type variables as substitutions which are visible only to // the outer context. if (type->is()) return; if (type->hasTypeVariable()) { SmallPtrSet typeVars; type->getTypeVariables(typeVars); // Some of the type variables could be non-representative, so // we need to recurse into `inferTypeVariables` to property // handle them. for (auto *typeVar : typeVars) inferVariables(typeVar); } } }; } void ConjunctionElement::findReferencedVariables( ConstraintSystem &cs, SmallPtrSetImpl &typeVars) const { auto referencedVars = Element->getTypeVariables(); typeVars.insert(referencedVars.begin(), referencedVars.end()); if (Element->getKind() != ConstraintKind::SyntacticElement) return; ASTNode element = Element->getSyntacticElement(); auto *locator = Element->getLocator(); ASTNode parent = locator->getAnchor(); if (auto *SVE = getAsExpr(parent)) { // Use a parent closure if we have one. This is needed to correctly handle // return statements that refer to an outer closure. if (auto *CE = dyn_cast(SVE->getDeclContext())) parent = CE; } TypeVariableRefFinder refFinder(cs, parent, Element->getElementContext(), typeVars); // If this is a pattern of `for-in` statement, let's walk into `for-in` // sequence expression because both elements are type-checked together. // // Correct expressions wouldn't have any type variables in sequence but // they could appear due to circular references or other incorrect syntax. if (isa(element)) { if (auto parent = locator->getLastElementAs()) { if (auto *forEach = getAsStmt(parent->getElement())) { if (auto *sequence = forEach->getSequence()) sequence->walk(refFinder); return; } } } if (auto *patternBinding = dyn_cast_or_null(element.dyn_cast())) { // Let's not walk into placeholder variable initializers, since they // are type-checked separately right now. if (isPlaceholderVar(patternBinding)) return; if (auto patternBindingElt = locator ->getLastElementAs()) { if (auto *init = patternBinding->getInit(patternBindingElt->getIndex())) init->walk(refFinder); return; } } if (isa(element) || isa(element) || isa(element) || element.isPattern(PatternKind::Expr) || element.isStmt(StmtKind::Return)) { element.walk(refFinder); } }