diff --git a/include/swift/Sema/Constraint.h b/include/swift/Sema/Constraint.h index a332172825244..aeb3a4f61e49b 100644 --- a/include/swift/Sema/Constraint.h +++ b/include/swift/Sema/Constraint.h @@ -171,12 +171,6 @@ enum class ConstraintKind : char { /// The key path type is chosen based on the selection of overloads for the /// member references along the path. KeyPath, - /// The first type is a function type, the second is the function's - /// input type. - FunctionInput, - /// The first type is a function type, the second is the function's - /// result type. - FunctionResult, /// The first type will be equal to the second type, but only when the /// second type has been fully determined (and mapped down to a concrete /// type). At that point, this constraint will be treated like an `Equal` @@ -691,8 +685,6 @@ class Constraint final : public llvm::ilist_node, case ConstraintKind::KeyPath: case ConstraintKind::KeyPathApplication: case ConstraintKind::Defaultable: - case ConstraintKind::FunctionInput: - case ConstraintKind::FunctionResult: return ConstraintClassification::TypeProperty; case ConstraintKind::Disjunction: diff --git a/include/swift/Sema/ConstraintLocator.h b/include/swift/Sema/ConstraintLocator.h index 58286d50b39e2..57aec34e998fd 100644 --- a/include/swift/Sema/ConstraintLocator.h +++ b/include/swift/Sema/ConstraintLocator.h @@ -960,6 +960,25 @@ class LocatorPathElt::KeyPathType final } }; +class LocatorPathElt::ConstructorMemberType final + : public StoredIntegerElement<1> { +public: + ConstructorMemberType(bool isShortFormOrSelfDelegating = false) + : StoredIntegerElement(ConstraintLocator::ConstructorMemberType, + isShortFormOrSelfDelegating) {} + + /// Whether this constructor overload is for a short-form init call such as + /// 'X(...)', or a 'self.init(...)' call. Such calls have additional ranking + /// rules. + bool isShortFormOrSelfDelegatingConstructor() const { + return bool(getValue()); + } + + static bool classof(const LocatorPathElt *elt) { + return elt->getKind() == ConstraintLocator::ConstructorMemberType; + } +}; + class LocatorPathElt::ClosureBodyElement final : public StoredPointerElement { public: diff --git a/include/swift/Sema/ConstraintLocatorPathElts.def b/include/swift/Sema/ConstraintLocatorPathElts.def index 0eb57ecf58223..527a977da6158 100644 --- a/include/swift/Sema/ConstraintLocatorPathElts.def +++ b/include/swift/Sema/ConstraintLocatorPathElts.def @@ -60,6 +60,9 @@ CUSTOM_LOCATOR_PATH_ELT(ClosureBody) /// The lookup for a constructor member. SIMPLE_LOCATOR_PATH_ELT(ConstructorMember) +/// The constructor member type in the lookup of a constructor. +CUSTOM_LOCATOR_PATH_ELT(ConstructorMemberType) + /// The desired contextual type passed in to the constraint system. CUSTOM_LOCATOR_PATH_ELT(ContextualType) diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index e009eee2c9a77..c4c2c8810a798 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -4751,13 +4751,6 @@ class ConstraintSystem { TypeMatchOptions flags, ConstraintLocatorBuilder locator); - /// Attempt to simplify a function input or result constraint. - SolutionKind simplifyFunctionComponentConstraint( - ConstraintKind kind, - Type first, Type second, - TypeMatchOptions flags, - ConstraintLocatorBuilder locator); - /// Attempt to simplify the BridgingConversion constraint. SolutionKind simplifyBridgingConstraint(Type type1, Type type2, diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index e132cc762d903..528ce68b84b7b 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -1336,8 +1336,6 @@ void PotentialBindings::infer(Constraint *constraint) { case ConstraintKind::EscapableFunctionOf: case ConstraintKind::OpenedExistentialOf: case ConstraintKind::KeyPath: - case ConstraintKind::FunctionInput: - case ConstraintKind::FunctionResult: case ConstraintKind::ClosureBodyElement: case ConstraintKind::Conjunction: // Constraints from which we can't do anything. diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 1787972004bbd..72fb440470304 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -1214,8 +1214,14 @@ namespace { auto *memberLoc = CS.getConstraintLocator(expr, ConstraintLocator::ConstructorMember); + auto *fnLoc = + CS.getConstraintLocator(expr, ConstraintLocator::ApplyFunction); + + auto *memberTypeLoc = CS.getConstraintLocator( + fnLoc, LocatorPathElt::ConstructorMemberType()); + auto *memberType = - CS.createTypeVariable(memberLoc, TVO_CanBindToNoEscape); + CS.createTypeVariable(memberTypeLoc, TVO_CanBindToNoEscape); CS.addValueMemberConstraint(MetatypeType::get(witnessType, ctx), DeclNameRef(constrName), memberType, CurDC, @@ -1228,10 +1234,9 @@ namespace { CS.getConstraintLocator(expr, ConstraintLocator::FunctionResult), TVO_CanBindToNoEscape); - CS.addConstraint( - ConstraintKind::ApplicableFunction, - FunctionType::get(params, resultType), memberType, - CS.getConstraintLocator(expr, ConstraintLocator::ApplyFunction)); + CS.addConstraint(ConstraintKind::ApplicableFunction, + FunctionType::get(params, resultType), memberType, + fnLoc); if (constr->isFailable()) return OptionalType::get(witnessType); @@ -1500,25 +1505,12 @@ namespace { // self.super = Super.init() baseTy = MetatypeType::get(baseTy, ctx); - auto methodTy = CS.createTypeVariable( - CS.getConstraintLocator(expr, - ConstraintLocator::ApplyFunction), - TVO_CanBindToNoEscape); - - // FIXME: Once TVO_PrefersSubtypeBinding is replaced with something - // better, we won't need the second type variable at all. - { - auto argTy = CS.createTypeVariable( - CS.getConstraintLocator(expr, - ConstraintLocator::ApplyArgument), - (TVO_CanBindToLValue | - TVO_CanBindToInOut | - TVO_CanBindToNoEscape | - TVO_PrefersSubtypeBinding)); - CS.addConstraint( - ConstraintKind::FunctionInput, methodTy, argTy, - CS.getConstraintLocator(expr)); - } + auto memberTypeLoc = CS.getConstraintLocator( + expr, LocatorPathElt::ConstructorMemberType( + /*shortFormOrSelfDelegating*/ true)); + + auto methodTy = + CS.createTypeVariable(memberTypeLoc, TVO_CanBindToNoEscape); CS.addValueMemberConstraint( baseTy, expr->getName(), methodTy, CurDC, diff --git a/lib/Sema/CSRanking.cpp b/lib/Sema/CSRanking.cpp index 0fd639e7f10d0..0b3ba4fb930fb 100644 --- a/lib/Sema/CSRanking.cpp +++ b/lib/Sema/CSRanking.cpp @@ -766,6 +766,40 @@ static void addKeyPathDynamicMemberOverloads( } } +/// Given the bound types of two constructor overloads, returns their parameter +/// list types as tuples to compare for solution ranking, or \c None if they +/// shouldn't be compared. +static Optional> +getConstructorParamsAsTuples(ASTContext &ctx, Type boundTy1, Type boundTy2) { + auto choiceTy1 = + boundTy1->lookThroughAllOptionalTypes()->getAs(); + auto choiceTy2 = + boundTy2->lookThroughAllOptionalTypes()->getAs(); + + // If the type variables haven't been bound to functions yet, let's not try + // and rank them. + if (!choiceTy1 || !choiceTy2) + return None; + + auto initParams1 = choiceTy1->getParams(); + auto initParams2 = choiceTy2->getParams(); + if (initParams1.size() != initParams2.size()) + return None; + + // Don't compare if there are variadic differences. This preserves the + // behavior of when we'd compare through matchTupleTypes with the parameter + // flags intact. + for (auto idx : indices(initParams1)) { + if (initParams1[idx].isVariadic() != initParams2[idx].isVariadic()) + return None; + } + auto tuple1 = AnyFunctionType::composeTuple(ctx, initParams1, + /*wantParamFlags*/ false); + auto tuple2 = AnyFunctionType::composeTuple(ctx, initParams2, + /*wantParamFlags*/ false); + return std::make_pair(tuple1, tuple2); +} + SolutionCompareResult ConstraintSystem::compareSolutions( ConstraintSystem &cs, ArrayRef solutions, const SolutionDiff &diff, unsigned idx1, unsigned idx2) { @@ -1127,11 +1161,23 @@ SolutionCompareResult ConstraintSystem::compareSolutions( for (const auto &binding1 : bindings1) { auto *typeVar = binding1.first; + auto *loc = typeVar->getImpl().getLocator(); + + // Check whether this is the overload type for a short-form init call + // 'X(...)' or 'self.init(...)' call. + auto isShortFormOrSelfDelegatingConstructorBinding = false; + if (auto initMemberTypeElt = + loc->getLastElementAs()) { + isShortFormOrSelfDelegatingConstructorBinding = + initMemberTypeElt->isShortFormOrSelfDelegatingConstructor(); + } // If the type variable isn't one for which we should be looking at the // bindings, don't. - if (!typeVar->getImpl().prefersSubtypeBinding()) + if (!typeVar->getImpl().prefersSubtypeBinding() && + !isShortFormOrSelfDelegatingConstructorBinding) { continue; + } // If both solutions have a binding for this type variable // let's consider it. @@ -1142,9 +1188,24 @@ SolutionCompareResult ConstraintSystem::compareSolutions( auto concreteType1 = binding1.second; auto concreteType2 = binding2->second; - if (!concreteType1->isEqual(concreteType2)) { - typeDiff.insert({typeVar, {concreteType1, concreteType2}}); + // For short-form and self-delegating init calls, we want to prefer + // parameter lists with subtypes over supertypes. To do this, compose tuples + // for the bound parameter lists, and compare them in the type diff. This + // logic preserves the behavior of when we used to bind the parameter list + // as a tuple to a TVO_PrefersSubtypeBinding type variable for such calls. + // FIXME: We should come up with a better way of doing this, though note we + // have some ranking and subtyping rules specific to tuples that we may need + // to preserve to avoid breaking source. + if (isShortFormOrSelfDelegatingConstructorBinding) { + auto diffs = getConstructorParamsAsTuples(cs.getASTContext(), + concreteType1, concreteType2); + if (!diffs) + continue; + std::tie(concreteType1, concreteType2) = *diffs; } + + if (!concreteType1->isEqual(concreteType2)) + typeDiff.insert({typeVar, {concreteType1, concreteType2}}); } for (auto &binding : typeDiff) { diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 5c3eb28c48c00..c2fe56bd712aa 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -1632,8 +1632,6 @@ ConstraintSystem::matchTupleTypes(TupleType *tuple1, TupleType *tuple2, case ConstraintKind::ValueMember: case ConstraintKind::ValueWitness: case ConstraintKind::BridgingConversion: - case ConstraintKind::FunctionInput: - case ConstraintKind::FunctionResult: case ConstraintKind::OneWayEqual: case ConstraintKind::OneWayBindParam: case ConstraintKind::DefaultClosureType: @@ -1773,8 +1771,6 @@ static bool matchFunctionRepresentations(FunctionType::ExtInfo einfo1, case ConstraintKind::UnresolvedValueMember: case ConstraintKind::ValueMember: case ConstraintKind::ValueWitness: - case ConstraintKind::FunctionInput: - case ConstraintKind::FunctionResult: case ConstraintKind::OneWayEqual: case ConstraintKind::OneWayBindParam: case ConstraintKind::DefaultClosureType: @@ -2178,8 +2174,6 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, case ConstraintKind::ValueMember: case ConstraintKind::ValueWitness: case ConstraintKind::BridgingConversion: - case ConstraintKind::FunctionInput: - case ConstraintKind::FunctionResult: case ConstraintKind::OneWayEqual: case ConstraintKind::OneWayBindParam: case ConstraintKind::DefaultClosureType: @@ -5266,8 +5260,6 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, case ConstraintKind::UnresolvedValueMember: case ConstraintKind::ValueMember: case ConstraintKind::ValueWitness: - case ConstraintKind::FunctionInput: - case ConstraintKind::FunctionResult: case ConstraintKind::OneWayEqual: case ConstraintKind::OneWayBindParam: case ConstraintKind::DefaultClosureType: @@ -6214,8 +6206,11 @@ ConstraintSystem::simplifyConstructionConstraint( auto fnLocator = getConstraintLocator(locator, ConstraintLocator::ApplyFunction); - auto memberType = createTypeVariable(fnLocator, - TVO_CanBindToNoEscape); + auto memberTypeLoc = + getConstraintLocator(fnLocator, LocatorPathElt::ConstructorMemberType( + /*shortFormOrSelfDelegating*/ true)); + + auto memberType = createTypeVariable(memberTypeLoc, TVO_CanBindToNoEscape); // The constructor will have function type T -> T2, for a fresh type // variable T. T2 is the result type provided via the construction @@ -6229,18 +6224,6 @@ ConstraintSystem::simplifyConstructionConstraint( fnLocator, ConstraintLocator::ConstructorMember)); - // FIXME: Once TVO_PrefersSubtypeBinding is replaced with something - // better, we won't need the second type variable at all. - { - auto argType = createTypeVariable( - getConstraintLocator(locator, ConstraintLocator::ApplyArgument), - (TVO_CanBindToLValue | - TVO_CanBindToInOut | - TVO_CanBindToNoEscape | - TVO_PrefersSubtypeBinding)); - addConstraint(ConstraintKind::FunctionInput, memberType, argType, locator); - } - addConstraint(ConstraintKind::ApplicableFunction, fnType, memberType, fnLocator); @@ -7067,71 +7050,6 @@ ConstraintSystem::simplifyOptionalObjectConstraint( return SolutionKind::Solved; } -/// Attempt to simplify a function input or result constraint. -ConstraintSystem::SolutionKind -ConstraintSystem::simplifyFunctionComponentConstraint( - ConstraintKind kind, - Type first, Type second, - TypeMatchOptions flags, - ConstraintLocatorBuilder locator) { - auto simplified = simplifyType(first); - auto simplifiedCopy = simplified; - - unsigned unwrapCount = 0; - if (shouldAttemptFixes()) { - while (auto objectTy = simplified->getOptionalObjectType()) { - simplified = objectTy; - - // Track how many times we do this so that we can record a fix for each. - ++unwrapCount; - } - - if (simplified->isPlaceholder()) { - if (auto *typeVar = second->getAs()) - recordPotentialHole(typeVar); - return SolutionKind::Solved; - } - } - - if (simplified->isTypeVariableOrMember()) { - if (!flags.contains(TMF_GenerateConstraints)) - return SolutionKind::Unsolved; - - addUnsolvedConstraint( - Constraint::create(*this, kind, simplified, second, - getConstraintLocator(locator))); - } else if (auto *funcTy = simplified->getAs()) { - // Equate it to the other type in the constraint. - Type type; - ConstraintLocator::PathElementKind locKind; - - if (kind == ConstraintKind::FunctionInput) { - type = AnyFunctionType::composeTuple(getASTContext(), - funcTy->getParams()); - locKind = ConstraintLocator::FunctionArgument; - } else if (kind == ConstraintKind::FunctionResult) { - type = funcTy->getResult(); - locKind = ConstraintLocator::FunctionResult; - } else { - llvm_unreachable("Bad function component constraint kind"); - } - - addConstraint(ConstraintKind::Bind, type, second, - locator.withPathElement(locKind)); - } else { - return SolutionKind::Error; - } - - if (unwrapCount > 0) { - auto *fix = ForceOptional::create(*this, simplifiedCopy, second, - getConstraintLocator(locator)); - if (recordFix(fix, /*impact=*/unwrapCount)) - return SolutionKind::Error; - } - - return SolutionKind::Solved; -} - static bool isForKeyPathSubscript(ConstraintSystem &cs, ConstraintLocator *locator) { if (!locator || !locator->getAnchor()) @@ -11338,7 +11256,10 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( ArgumentLists.insert({memberLoc, argList}); } - auto *memberTy = createTypeVariable(memberLoc, TVO_CanBindToNoEscape); + auto *memberTypeLoc = getConstraintLocator( + applicationLoc, LocatorPathElt::ConstructorMemberType()); + + auto *memberTy = createTypeVariable(memberTypeLoc, TVO_CanBindToNoEscape); addValueMemberConstraint(MetatypeType::get(type2, getASTContext()), DeclNameRef(DeclBaseName::createConstructor()), @@ -12054,11 +11975,6 @@ ConstraintSystem::addConstraintImpl(ConstraintKind kind, Type first, case ConstraintKind::PropertyWrapper: return simplifyPropertyWrapperConstraint(first, second, subflags, locator); - case ConstraintKind::FunctionInput: - case ConstraintKind::FunctionResult: - return simplifyFunctionComponentConstraint(kind, first, second, - subflags, locator); - case ConstraintKind::OneWayEqual: case ConstraintKind::OneWayBindParam: return simplifyOneWayConstraint(kind, first, second, subflags, locator); @@ -12588,14 +12504,6 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) { /*flags*/ None, constraint.getLocator()); - case ConstraintKind::FunctionInput: - case ConstraintKind::FunctionResult: - return simplifyFunctionComponentConstraint(constraint.getKind(), - constraint.getFirstType(), - constraint.getSecondType(), - /*flags*/ None, - constraint.getLocator()); - case ConstraintKind::Disjunction: case ConstraintKind::Conjunction: // {Dis, Con}junction constraints are never solved here. diff --git a/lib/Sema/Constraint.cpp b/lib/Sema/Constraint.cpp index 3d965e644f0c0..5cde5b9b54d4d 100644 --- a/lib/Sema/Constraint.cpp +++ b/lib/Sema/Constraint.cpp @@ -73,8 +73,6 @@ Constraint::Constraint(ConstraintKind Kind, Type First, Type Second, case ConstraintKind::EscapableFunctionOf: case ConstraintKind::OpenedExistentialOf: case ConstraintKind::OptionalObject: - case ConstraintKind::FunctionInput: - case ConstraintKind::FunctionResult: case ConstraintKind::OneWayEqual: case ConstraintKind::OneWayBindParam: case ConstraintKind::UnresolvedMemberChainBase: @@ -157,8 +155,6 @@ Constraint::Constraint(ConstraintKind Kind, Type First, Type Second, Type Third, case ConstraintKind::BindOverload: case ConstraintKind::Disjunction: case ConstraintKind::Conjunction: - case ConstraintKind::FunctionInput: - case ConstraintKind::FunctionResult: case ConstraintKind::OneWayEqual: case ConstraintKind::OneWayBindParam: case ConstraintKind::DefaultClosureType: @@ -302,8 +298,6 @@ Constraint *Constraint::clone(ConstraintSystem &cs) const { case ConstraintKind::DynamicCallableApplicableFunction: case ConstraintKind::OptionalObject: case ConstraintKind::Defaultable: - case ConstraintKind::FunctionInput: - case ConstraintKind::FunctionResult: case ConstraintKind::OneWayEqual: case ConstraintKind::OneWayBindParam: case ConstraintKind::DefaultClosureType: @@ -448,10 +442,6 @@ void Constraint::print(llvm::raw_ostream &Out, SourceManager *sm) const { break; case ConstraintKind::OptionalObject: Out << " optional with object type "; break; - case ConstraintKind::FunctionInput: - Out << " bind function input of "; break; - case ConstraintKind::FunctionResult: - Out << " bind function result of "; break; case ConstraintKind::BindOverload: { Out << " bound to "; auto overload = getOverloadChoice(); @@ -668,8 +658,6 @@ gatherReferencedTypeVars(Constraint *constraint, case ConstraintKind::LiteralConformsTo: case ConstraintKind::TransitivelyConformsTo: case ConstraintKind::SelfObjectOfProtocol: - case ConstraintKind::FunctionInput: - case ConstraintKind::FunctionResult: case ConstraintKind::OneWayEqual: case ConstraintKind::OneWayBindParam: case ConstraintKind::DefaultClosureType: diff --git a/lib/Sema/ConstraintLocator.cpp b/lib/Sema/ConstraintLocator.cpp index 677b596407f2d..c2424a69c2597 100644 --- a/lib/Sema/ConstraintLocator.cpp +++ b/lib/Sema/ConstraintLocator.cpp @@ -47,6 +47,7 @@ unsigned LocatorPathElt::getNewSummaryFlags() const { case ConstraintLocator::ClosureResult: case ConstraintLocator::ClosureBody: case ConstraintLocator::ConstructorMember: + case ConstraintLocator::ConstructorMemberType: case ConstraintLocator::ResultBuilderBodyResult: case ConstraintLocator::InstanceType: case ConstraintLocator::AutoclosureResult: @@ -322,6 +323,14 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const { out << "constructor member"; break; + case ConstructorMemberType: { + auto memberTypeElt = elt.castTo(); + out << "constructor member type"; + if (memberTypeElt.isShortFormOrSelfDelegatingConstructor()) + out << " (for short-form or self.init call)"; + break; + } + case FunctionArgument: out << "function argument"; break; diff --git a/test/Constraints/pr39543.swift b/test/Constraints/pr39543.swift new file mode 100644 index 0000000000000..8acce5a2a4147 --- /dev/null +++ b/test/Constraints/pr39543.swift @@ -0,0 +1,11 @@ +// RUN: %target-swift-ide-test -code-completion -source-filename=%s -code-completion-token=CC + +// PR #39543: Make sure we can complete in this position without crashing. +extension String { + init(format: String, _: Any) { fatalError() } +} +extension RandomAccessCollection { + func foo() { + print(String(format: "", Int(distance(from:#^CC^# startIndex, to: startIndex)))) + } +}