diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 7640401191a88..00ad1842168b9 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -97,6 +97,8 @@ ERROR(expected_argument_in_contextual_member,none, "member %0 expects argument of type %1", (DeclName, Type)) ERROR(expected_parens_in_contextual_member,none, "member %0 is a function; did you mean to call it?", (DeclName)) +ERROR(expected_parens_in_contextual_member_type,none, + "member %0 is a function that produces expected type %1; did you mean to call it?", (DeclName, Type)) ERROR(expected_result_in_contextual_member,none, "member %0 in %2 produces result of type %1, but context expects %2", diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 6bbc088b4bcbd..65f43d7284438 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -2703,17 +2703,16 @@ namespace { } Expr *visitUnresolvedMemberExpr(UnresolvedMemberExpr *expr) { - // Dig out the type of the base, which will be the result type of this - // expression. If constraint solving resolved this to an UnresolvedType, + // If constraint solving resolved the base type to an UnresolvedType, // then we're in an ambiguity tolerant mode used for diagnostic // generation. Just leave this as an unresolved member reference. - Type resultTy = simplifyType(cs.getType(expr)); - if (resultTy->getRValueType()->is()) { - cs.setType(expr, resultTy); + Type baseTy = simplifyType(solution.getUnresolvedMemberBaseType(expr)); + + if (baseTy->getRValueType()->is()) { + cs.setType(expr, baseTy); return expr; } - Type baseTy = resultTy->getRValueType(); auto &ctx = cs.getASTContext(); // Find the selected member. @@ -2771,7 +2770,8 @@ namespace { diagnoseAmbiguousNominalMember(baseTy, result); } - return coerceToType(result, resultTy, cs.getConstraintLocator(expr)); + return coerceToType(result, simplifyType(cs.getType(expr)), + cs.getConstraintLocator(expr)); } /// Diagnose if the base type is optional, we're referring to a nominal @@ -8459,6 +8459,15 @@ void Solution::setExprTypes(Expr *expr) const { expr->walk(SET); } +Type Solution::getUnresolvedMemberBaseType(UnresolvedMemberExpr *expr) const { + auto result = unresolvedMemberBaseTypes.find(expr); + if (result != unresolvedMemberBaseTypes.end()) + return result->second; + + auto &cs = getConstraintSystem(); + return cs.getUnresolvedMemberBaseType(expr); +} + /// MARK: SolutionResult implementation. SolutionResult SolutionResult::forSolved(Solution &&solution) { diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index a73e1f60bb484..cfb20eb7f2c98 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -39,7 +39,20 @@ void ConstraintSystem::inferTransitiveSupertypeBindings( return rhs->getAs() == typeVar; }); - if (subtypeOf.empty()) + llvm::SmallVector convertibleFrom; + if (!bindings.HasNonPreferredDefaultBindings) + llvm::copy_if(bindings.Sources, std::back_inserter(convertibleFrom), + [&](const Constraint *constraint) -> bool { + if (constraint->getKind() != ConstraintKind::Conversion && + constraint->getKind() + != ConstraintKind::ArgumentConversion) + return false; + + auto rhs = simplifyType(constraint->getSecondType()); + return rhs->getAs() == typeVar; + }); + + if (convertibleFrom.empty() && subtypeOf.empty()) return; // We need to make sure that there are no duplicate bindings in the @@ -48,36 +61,47 @@ void ConstraintSystem::inferTransitiveSupertypeBindings( for (const auto &binding : bindings.Bindings) existingTypes.insert(binding.BindingType->getCanonicalType()); - for (auto *constraint : subtypeOf) { - auto *tv = - simplifyType(constraint->getFirstType())->getAs(); - if (!tv) - continue; - - auto relatedBindings = inferredBindings.find(tv); - if (relatedBindings == inferredBindings.end()) - continue; + auto addTransitiveBindings = [&](llvm::SmallVector sources, + AllowedBindingKind additionalAllowedKind) { + for (auto *constraint : sources) { + auto *tv = + simplifyType(constraint->getFirstType())->getAs(); + if (!tv) + continue; - for (auto &binding : relatedBindings->getSecond().Bindings) { - // We need the binding kind for the potential binding to - // either be Exact or Supertypes in order for it to make sense - // to add Supertype bindings based on the relationship between - // our type variables. - if (binding.Kind != AllowedBindingKind::Exact && - binding.Kind != AllowedBindingKind::Supertypes) + auto relatedBindings = inferredBindings.find(tv); + if (relatedBindings == inferredBindings.end()) continue; - auto type = binding.BindingType; + for (auto &binding : relatedBindings->getSecond().Bindings) { + // We need the binding kind for the potential binding to + // either be Exact or Supertypes in order for it to make sense + // to add Supertype bindings based on the relationship between + // our type variables. + if (binding.Kind != AllowedBindingKind::Exact && + binding.Kind != additionalAllowedKind) + continue; - if (!existingTypes.insert(type->getCanonicalType()).second) - continue; + auto type = binding.BindingType; - if (ConstraintSystem::typeVarOccursInType(typeVar, type)) - continue; + if (!existingTypes.insert(type->getCanonicalType()).second) + continue; - bindings.addPotentialBinding( - binding.withSameSource(type, AllowedBindingKind::Supertypes)); + if (ConstraintSystem::typeVarOccursInType(typeVar, type)) + continue; + + bindings.addPotentialBinding( + binding.withSameSource(type, AllowedBindingKind::Supertypes)); + } } + }; + + if (!subtypeOf.empty()) { + addTransitiveBindings(subtypeOf, AllowedBindingKind::Supertypes); + } + + if (!convertibleFrom.empty()) { + addTransitiveBindings(convertibleFrom, AllowedBindingKind::Subtypes); } } @@ -202,6 +226,9 @@ void ConstraintSystem::PotentialBindings::addPotentialBinding( if (binding.isDefaultableBinding()) ++NumDefaultableBindings; + if (!binding.isPreferredDefaultBinding()) + HasNonPreferredDefaultBindings = true; + Bindings.push_back(std::move(binding)); } @@ -487,7 +514,8 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) const { case ConstraintKind::Conversion: case ConstraintKind::ArgumentConversion: case ConstraintKind::OperatorArgumentConversion: - case ConstraintKind::OptionalObject: { + case ConstraintKind::OptionalObject: + case ConstraintKind::PreferredDefault: { // If there is a `bind param` constraint associated with // current type variable, result should be aware of that // fact. Binding set might be incomplete until @@ -555,6 +583,7 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) const { case ConstraintKind::FunctionInput: case ConstraintKind::FunctionResult: case ConstraintKind::OpaqueUnderlyingType: + case ConstraintKind::NominalEqual: // Constraints from which we can't do anything. break; @@ -575,7 +604,7 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) const { } case ConstraintKind::Defaultable: - case ConstraintKind::DefaultClosureType: + case ConstraintKind::DefaultClosureType: { // Do these in a separate pass. if (getFixedTypeRecursive(constraint->getFirstType(), true) ->getAs() == typeVar) { @@ -583,7 +612,7 @@ ConstraintSystem::getPotentialBindings(TypeVariableType *typeVar) const { hasNonDependentMemberRelationalConstraints = true; } break; - + } case ConstraintKind::Disjunction: // FIXME: Recurse into these constraints to see whether this // type variable is fully bound by any of them. diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 0434172c9b4a4..b34b986c5f7f2 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -1106,14 +1106,6 @@ bool MissingOptionalUnwrapFailure::diagnoseAsError() { auto *anchor = castToExpr(getAnchor()); - // If this is an unresolved member expr e.g. `.foo` its - // base type is going to be the same as result type minus - // r-value adjustment because base could be an l-value type. - // We want to fix both cases by only diagnose one of them, - // otherwise this is just going to result in a duplcate diagnostic. - if (getLocator()->isLastElement()) - return false; - if (auto assignExpr = dyn_cast(anchor)) anchor = assignExpr->getSrc(); @@ -2034,10 +2026,19 @@ bool ContextualFailure::diagnoseAsError() { return false; } - case ConstraintLocator::RValueAdjustment: { + case ConstraintLocator::UnresolvedMemberChainResult: { auto &solution = getSolution(); - auto overload = getOverloadChoiceIfAvailable( - getConstraintLocator(anchor, ConstraintLocator::UnresolvedMember)); + auto member = anchor; + if (auto *CE = getAsExpr(anchor)) + member = CE->getFn(); + + auto kind = ConstraintLocator::Member; + if (isExpr(anchor)) + kind = ConstraintLocator::UnresolvedMember; + else if (isExpr(anchor)) + kind = ConstraintLocator::SubscriptMember; + auto overload = getOverloadChoiceIfAvailable(getConstraintLocator(member, + kind)); if (!(overload && overload->choice.isDecl())) return false; @@ -2072,13 +2073,22 @@ bool ContextualFailure::diagnoseAsError() { }); if (numMissingArgs == 0 || numMissingArgs > 1) { - auto diagnostic = emitDiagnostic( - diag::expected_parens_in_contextual_member, choice->getName()); - - // If there are no parameters we can suggest a fix-it - // to form an explicit call. - if (numMissingArgs == 0) - diagnostic.fixItInsertAfter(getSourceRange().End, "()"); + auto applyFixIt = [&](InFlightDiagnostic &diagnostic) { + // If there are no parameters we can suggest a fix-it + // to form an explicit call. + if (numMissingArgs == 0) + diagnostic.fixItInsertAfter(getSourceRange().End, "()"); + }; + if (fnType->getResult()->isEqual(toType)) { + auto diag = emitDiagnostic( + diag::expected_parens_in_contextual_member_type, + choice->getName(), fnType->getResult()); + applyFixIt(diag); + } else { + auto diag = emitDiagnostic(diag::expected_parens_in_contextual_member, + choice->getName()); + applyFixIt(diag); + } } else { emitDiagnostic(diag::expected_argument_in_contextual_member, choice->getName(), params.front().getPlainType()); @@ -2296,6 +2306,9 @@ bool ContextualFailure::diagnoseMissingFunctionCall() const { if (getLocator()->isLastElement()) return false; + if (getLocator()->isLastElement()) + return false; + auto *srcFT = getFromType()->getAs(); if (!srcFT || !(srcFT->getParams().empty() || diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index a4ea12163c394..04ab4af9f8954 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -990,6 +990,38 @@ namespace { return tv; } + Type addUnresolvedMemberChainConstraints(Expr *expr, Type resultTy, + unsigned additionalOptions = 0) { + // If this is a member chain hanging off of an UnresolvedMemberExpr, + // and we're at the last element of the chain, then the contextual type + // (represented with a new type variable) must equal the base type. + if (CS.isMemberChainTail(expr)) { + auto *chainBaseExpr = CS.getMemberChainBase(expr); + if (auto *UME = dyn_cast(chainBaseExpr)) { + // Create a new type variable representing the result of the chain. + auto locator = CS.getConstraintLocator(expr, + ConstraintLocator::UnresolvedMemberChainResult); + auto chainResultTy = CS.createTypeVariable(locator, + additionalOptions | TVO_CanBindToHole | TVO_CanBindToNoEscape); + auto chainBaseTy = CS.getUnresolvedMemberBaseType(UME); + + // The result of this element of the chain must be convertible to the + // contextual type, and the contextual type must be equal to the base + // (up to generic arguments). + CS.addConstraint(ConstraintKind::Conversion, resultTy, chainResultTy, + locator); + CS.addConstraint(ConstraintKind::NominalEqual, chainBaseTy, + chainResultTy, + CS.getConstraintLocator(UME, ConstraintLocator::MemberRefBase)); + + return chainResultTy; + } + } + + // Otherwise, just use the proposed result type. + return resultTy; + } + /// Add constraints for a subscript operation. Type addSubscriptConstraints( Expr *anchor, Type baseTy, Expr *index, @@ -1542,15 +1574,17 @@ namespace { expr->getArgument() ? FunctionRefKind::DoubleApply : FunctionRefKind::Compound; - auto memberLocator - = CS.getConstraintLocator(expr, ConstraintLocator::UnresolvedMember); + auto memberLocator = CS.getConstraintLocator(expr, + ConstraintLocator::UnresolvedMember); // Since base type in this case is completely dependent on context it // should be marked as a potential hole. - auto baseTy = CS.createTypeVariable(baseLocator, TVO_CanBindToNoEscape | - TVO_CanBindToHole); - auto memberTy = CS.createTypeVariable( - memberLocator, TVO_CanBindToLValue | TVO_CanBindToNoEscape); + auto baseTy = CS.createTypeVariable(baseLocator, + TVO_CanBindToHole | TVO_CanBindToNoEscape); + CS.setUnresolvedMemberBaseType(expr, baseTy); + + auto memberTy = CS.createTypeVariable(memberLocator, + TVO_CanBindToLValue | TVO_CanBindToNoEscape); // An unresolved member expression '.member' is modeled as a value member // constraint @@ -1566,14 +1600,10 @@ namespace { // If there is an argument, apply it. if (auto arg = expr->getArgument()) { - // The result type of the function must be convertible to the base type. - // TODO: we definitely want this to include ImplicitlyUnwrappedOptional; - // does it need to include everything else in the world? + // Create a new type variable for the result of the function. auto outputTy = CS.createTypeVariable( CS.getConstraintLocator(expr, ConstraintLocator::FunctionResult), TVO_CanBindToNoEscape); - CS.addConstraint(ConstraintKind::Conversion, outputTy, baseTy, - CS.getConstraintLocator(expr, ConstraintLocator::RValueAdjustment)); // The function/enum case must be callable with the given argument. @@ -1592,23 +1622,13 @@ namespace { CS.getConstraintLocator(expr), {expr->getArgumentLabels(), expr->getUnlabeledTrailingClosureIndex()}); - return baseTy; + return addUnresolvedMemberChainConstraints(expr, outputTy); } - - // Otherwise, the member needs to be convertible to the base type. - CS.addConstraint(ConstraintKind::Conversion, memberTy, baseTy, - CS.getConstraintLocator(expr, ConstraintLocator::RValueAdjustment)); - // The member type also needs to be convertible to the context type, which - // preserves lvalue-ness. - auto resultTy = CS.createTypeVariable(memberLocator, - TVO_CanBindToLValue | - TVO_CanBindToNoEscape); - CS.addConstraint(ConstraintKind::Conversion, memberTy, resultTy, - memberLocator); - CS.addConstraint(ConstraintKind::Equal, resultTy, baseTy, - memberLocator); - return resultTy; + // Otherwise, add the usual constraints for an element of an unresolved + // member chain. + return addUnresolvedMemberChainConstraints(expr, memberTy, + TVO_CanBindToLValue); } Type visitUnresolvedDotExpr(UnresolvedDotExpr *expr) { @@ -1672,9 +1692,13 @@ namespace { return methodTy; } - return addMemberRefConstraints(expr, expr->getBase(), expr->getName(), - expr->getFunctionRefKind(), - expr->getOuterAlternatives()); + auto resultTy = addMemberRefConstraints(expr, expr->getBase(), + expr->getName(), + expr->getFunctionRefKind(), + expr->getOuterAlternatives()); + + return addUnresolvedMemberChainConstraints(expr, resultTy, + TVO_CanBindToLValue); } Type visitUnresolvedSpecializeExpr(UnresolvedSpecializeExpr *expr) { @@ -1830,10 +1854,13 @@ namespace { if (!isValidBaseOfMemberRef(base, diag::cannot_subscript_nil_literal)) return nullptr; - return addSubscriptConstraints(expr, CS.getType(base), - expr->getIndex(), - decl, expr->getArgumentLabels(), - expr->getUnlabeledTrailingClosureIndex()); + auto resultTy = addSubscriptConstraints(expr, CS.getType(base), + expr->getIndex(), + decl, expr->getArgumentLabels(), + expr->getUnlabeledTrailingClosureIndex()); + + return addUnresolvedMemberChainConstraints(expr, resultTy, + TVO_CanBindToLValue); } Type visitArrayExpr(ArrayExpr *expr) { @@ -2948,6 +2975,10 @@ namespace { resultType = fixedType; } + // If the ApplyExpr is a CallExpr, add chain constraints as necessary. + if (isa(expr)) + return addUnresolvedMemberChainConstraints(expr, resultType); + return resultType; } @@ -3263,7 +3294,8 @@ namespace { CS.addConstraint(ConstraintKind::OptionalObject, CS.getType(expr->getSubExpr()), objectTy, locator); - return objectTy; + return addUnresolvedMemberChainConstraints(expr, objectTy, + TVO_CanBindToLValue); } Type visitOptionalEvaluationExpr(OptionalEvaluationExpr *expr) { @@ -3298,7 +3330,7 @@ namespace { CS.addConstraint(ConstraintKind::OptionalObject, CS.getType(expr->getSubExpr()), objectTy, locator); - return objectTy; + return addUnresolvedMemberChainConstraints(expr, objectTy); } Type visitOpenExistentialExpr(OpenExistentialExpr *expr) { diff --git a/lib/Sema/CSRanking.cpp b/lib/Sema/CSRanking.cpp index d42a49b6eefe3..7dc1c0ea6a962 100644 --- a/lib/Sema/CSRanking.cpp +++ b/lib/Sema/CSRanking.cpp @@ -69,10 +69,14 @@ void ConstraintSystem::increaseScore(ScoreKind kind, unsigned value) { log << "function conversion"; break; + case SK_NonPreferredDefault: + log << "non-preferred default "; + break; + case SK_NonDefaultLiteral: log << "non-default literal"; break; - + case SK_CollectionUpcastConversion: log << "collection upcast conversion"; break; diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 0f1279a2a578a..c8609c537a8b6 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -1323,6 +1323,8 @@ ConstraintSystem::matchTupleTypes(TupleType *tuple1, TupleType *tuple2, case ConstraintKind::FunctionResult: case ConstraintKind::OneWayEqual: case ConstraintKind::DefaultClosureType: + case ConstraintKind::NominalEqual: + case ConstraintKind::PreferredDefault: llvm_unreachable("Not a conversion"); } @@ -1388,6 +1390,8 @@ static bool matchFunctionRepresentations(FunctionTypeRepresentation rep1, case ConstraintKind::FunctionResult: case ConstraintKind::OneWayEqual: case ConstraintKind::DefaultClosureType: + case ConstraintKind::NominalEqual: + case ConstraintKind::PreferredDefault: return false; } @@ -1666,6 +1670,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, case ConstraintKind::BindParam: case ConstraintKind::BindToPointerType: case ConstraintKind::Equal: + case ConstraintKind::PreferredDefault: subKind = kind; break; @@ -1700,6 +1705,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, case ConstraintKind::FunctionResult: case ConstraintKind::OneWayEqual: case ConstraintKind::DefaultClosureType: + case ConstraintKind::NominalEqual: llvm_unreachable("Not a relational constraint"); } @@ -4025,22 +4031,12 @@ bool ConstraintSystem::repairFailures( break; } - // Unresolved member type mismatches are handled when - // r-value adjustment constraint fails. - case ConstraintLocator::UnresolvedMember: - return true; - - case ConstraintLocator::RValueAdjustment: { - if (!isExpr(anchor)) - break; - + case ConstraintLocator::UnresolvedMemberChainResult: { if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes, locator)) break; - // r-value adjustment is used to connect base type of - // unresolved member to its output type, if there is - // a type mismatch here it's contextual e.g. + // If there is a type mismatch here it's contextual e.g., // `let x: E = .foo(42)`, where `.foo` is a member of `E` // but produces an incorrect type. auto *fix = IgnoreContextualType::create(*this, lhs, rhs, @@ -4339,6 +4335,8 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, case ConstraintKind::FunctionResult: case ConstraintKind::OneWayEqual: case ConstraintKind::DefaultClosureType: + case ConstraintKind::NominalEqual: + case ConstraintKind::PreferredDefault: llvm_unreachable("Not a relational constraint"); } } @@ -6423,27 +6421,32 @@ static ConstraintFix *validateInitializerRef(ConstraintSystem &cs, } } // Initializer reference which requires contextual base type e.g. - // `.init(...)`. + // `.init(...)`. Could also be a nested type or typealias being constructed + // via implicit member syntax, e.g., `let _: Base = .Nested()` where + // `Base.Nested: Base`. } else if (auto *UME = getAsExpr(anchor)) { - // We need to find type variable which represents contextual base. - auto *baseLocator = cs.getConstraintLocator( - UME, locatorEndsWith(locator, ConstraintLocator::ConstructorMember) - ? ConstraintLocator::UnresolvedMember - : ConstraintLocator::MemberRefBase); - - // FIXME: Type variables responsible for contextual base could be cached - // in the constraint system to speed up lookup. - auto result = llvm::find_if( - cs.getTypeVariables(), [&baseLocator](const TypeVariableType *typeVar) { - return typeVar->getImpl().getLocator() == baseLocator; - }); + // If we're accessing a nested type to perform the construction implicitly, + // then the type we're constructing may not actually be the base of the + // UnresolvedMemberExpr--instead, it will be the type of the nested type + // member. + if (locatorEndsWith(locator, ConstraintLocator::ConstructorMember)) { + auto *baseLocator = cs.getConstraintLocator(UME, + ConstraintLocator::UnresolvedMember); + auto result = llvm::find_if( + cs.getTypeVariables(), [&baseLocator](const TypeVariableType *typeVar) { + return typeVar->getImpl().getLocator() == baseLocator; + }); + assert(result != cs.getTypeVariables().end()); + baseType = cs.simplifyType(*result); - assert(result != cs.getTypeVariables().end()); - baseType = cs.simplifyType(*result)->getRValueType(); - // Constraint for member base is formed as '$T.Type[.(anchor)) { // Key path can't refer to initializers e.g. `\Type.init` return AllowInvalidRefInKeyPath::forRef(cs, init, locator); @@ -6964,14 +6967,14 @@ ConstraintSystem::simplifyValueWitnessConstraint( } ConstraintSystem::SolutionKind ConstraintSystem::simplifyDefaultableConstraint( - Type first, Type second, TypeMatchOptions flags, + ConstraintKind kind, Type first, Type second, TypeMatchOptions flags, ConstraintLocatorBuilder locator) { first = getFixedTypeRecursive(first, flags, true); if (first->isTypeVariableOrMember()) { if (flags.contains(TMF_GenerateConstraints)) { addUnsolvedConstraint( - Constraint::create(*this, ConstraintKind::Defaultable, first, second, + Constraint::create(*this, kind, first, second, getConstraintLocator(locator))); return SolutionKind::Solved; } @@ -6979,6 +6982,14 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyDefaultableConstraint( return SolutionKind::Unsolved; } + if (kind == ConstraintKind::PreferredDefault) { + second = getFixedTypeRecursive(second, flags, true); + if (second->isTypeVariableOrMember()) + return SolutionKind::Unsolved; + else if (!first->isEqual(second)) + increaseScore(SK_NonPreferredDefault); + } + // Otherwise, any type is fine. return SolutionKind::Solved; } @@ -7039,6 +7050,53 @@ ConstraintSystem::simplifyOneWayConstraint( locator); } +ConstraintSystem::SolutionKind +ConstraintSystem::simplifyNominalEqualConstraint( + ConstraintKind kind, + Type first, Type second, TypeMatchOptions flags, + ConstraintLocatorBuilder locator) { + // Determine whether the second type has been simplified to a concrete type + // yet. If not, we can't do anything here. + Type secondSimplified = simplifyType(second); + if (secondSimplified->isTypeVariableOrMember()) { + if (flags.contains(TMF_GenerateConstraints)) { + addUnsolvedConstraint( + Constraint::create(*this, kind, first, second, + getConstraintLocator(locator))); + return SolutionKind::Solved; + } + + return SolutionKind::Unsolved; + } + + // If we've simplified the second type to a bound generic type, generate new + // type variables for the arguments of the first type. + if (auto generic2 = secondSimplified->getAs()) { + // Optionals should not receive this treatment. + if (!secondSimplified->getOptionalObjectType()) { + SmallVector args1; + auto args2 = generic2->getGenericArgs(); + for (unsigned i = 0; i < args2.size(); ++i) { + auto arg2 = args2[i]; + auto *argLocator = getConstraintLocator(locator, + LocatorPathElt::GenericArgument(i)); + auto arg1 = createTypeVariable(argLocator, 0); + addConstraint(ConstraintKind::PreferredDefault, arg1, arg2, + argLocator); + args1.push_back(arg1); + } + + auto generic1 = BoundGenericType::get(generic2->getDecl(), + generic2->getParent(), + args1); + return matchTypes(first, generic1, ConstraintKind::Equal, flags, locator); + } + } + + return matchTypes(first, secondSimplified, ConstraintKind::Equal, flags, + locator); +} + static Type getFunctionBuilderTypeFor(ConstraintSystem &cs, unsigned paramIdx, ConstraintLocator *calleeLocator) { auto selectedOverload = cs.findSelectedOverloadFor(calleeLocator); @@ -9534,6 +9592,12 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( impact = 10; } + if (locator->isLastElement()) { + // If this is a contextual failure for an unresolved member, then increase + // the impact to attempt other fixes first and avoid ambiguity. + impact = 5; + } + if (recordFix(fix, impact)) return SolutionKind::Error; @@ -9654,7 +9718,9 @@ ConstraintSystem::addConstraintImpl(ConstraintKind kind, Type first, return simplifyOptionalObjectConstraint(first, second, subflags, locator); case ConstraintKind::Defaultable: - return simplifyDefaultableConstraint(first, second, subflags, locator); + case ConstraintKind::PreferredDefault: + return simplifyDefaultableConstraint(kind, first, second, subflags, + locator); case ConstraintKind::FunctionInput: case ConstraintKind::FunctionResult: @@ -9664,6 +9730,10 @@ ConstraintSystem::addConstraintImpl(ConstraintKind kind, Type first, case ConstraintKind::OneWayEqual: return simplifyOneWayConstraint(kind, first, second, subflags, locator); + case ConstraintKind::NominalEqual: + return simplifyNominalEqualConstraint(kind, first, second, subflags, + locator); + case ConstraintKind::ValueMember: case ConstraintKind::UnresolvedValueMember: case ConstraintKind::ValueWitness: @@ -10142,7 +10212,9 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) { constraint.getLocator()); case ConstraintKind::Defaultable: - return simplifyDefaultableConstraint(constraint.getFirstType(), + case ConstraintKind::PreferredDefault: + return simplifyDefaultableConstraint(constraint.getKind(), + constraint.getFirstType(), constraint.getSecondType(), TMF_GenerateConstraints, constraint.getLocator()); @@ -10172,6 +10244,13 @@ ConstraintSystem::simplifyConstraint(const Constraint &constraint) { constraint.getSecondType(), TMF_GenerateConstraints, constraint.getLocator()); + + case ConstraintKind::NominalEqual: + return simplifyNominalEqualConstraint(constraint.getKind(), + constraint.getFirstType(), + constraint.getSecondType(), + TMF_GenerateConstraints, + constraint.getLocator()); } llvm_unreachable("Unhandled ConstraintKind in switch."); diff --git a/lib/Sema/CSSolver.cpp b/lib/Sema/CSSolver.cpp index b010ef5ac298f..3229f622e68d5 100644 --- a/lib/Sema/CSSolver.cpp +++ b/lib/Sema/CSSolver.cpp @@ -166,6 +166,10 @@ Solution ConstraintSystem::finalize() { solution.nodeTypes.insert(nodeType); } + for (auto &baseType : UnresolvedMemberBaseTypes) { + solution.unresolvedMemberBaseTypes.insert(baseType); + } + // Remember contextual types. solution.contextualTypes.assign( contextualTypes.begin(), contextualTypes.end()); @@ -236,6 +240,11 @@ void ConstraintSystem::applySolution(const Solution &solution) { setType(nodeType.first, nodeType.second); } + // Add the unresolved member base types back. + for (auto &baseType : solution.unresolvedMemberBaseTypes) { + setUnresolvedMemberBaseType(baseType.first, baseType.second); + } + // Add the contextual types. for (const auto &contextualType : solution.contextualTypes) { if (!getContextualTypeInfo(contextualType.first)) { @@ -456,6 +465,7 @@ ConstraintSystem::SolverScope::SolverScope(ConstraintSystem &cs) numOpenedExistentialTypes = cs.OpenedExistentialTypes.size(); numDefaultedConstraints = cs.DefaultedConstraints.size(); numAddedNodeTypes = cs.addedNodeTypes.size(); + numUnresolvedMemberBaseTypes = cs.UnresolvedMemberBaseTypes.size(); numCheckedConformances = cs.CheckedConformances.size(); numDisabledConstraints = cs.solverState->getNumDisabledConstraints(); numFavoredConstraints = cs.solverState->getNumFavoredConstraints(); @@ -530,6 +540,9 @@ ConstraintSystem::SolverScope::~SolverScope() { } truncate(cs.addedNodeTypes, numAddedNodeTypes); + // Remove any unresolved member base types. + truncate(cs.UnresolvedMemberBaseTypes, numUnresolvedMemberBaseTypes); + // Remove any conformances checked along the current path. truncate(cs.CheckedConformances, numCheckedConformances); @@ -1691,6 +1704,8 @@ void ConstraintSystem::ArgumentInfoCollector::walk(Type argType) { case ConstraintKind::Defaultable: case ConstraintKind::OneWayEqual: case ConstraintKind::DefaultClosureType: + case ConstraintKind::NominalEqual: + case ConstraintKind::PreferredDefault: break; } } diff --git a/lib/Sema/Constraint.cpp b/lib/Sema/Constraint.cpp index 99e433a171380..37d4b852b7ab5 100644 --- a/lib/Sema/Constraint.cpp +++ b/lib/Sema/Constraint.cpp @@ -66,6 +66,8 @@ Constraint::Constraint(ConstraintKind Kind, Type First, Type Second, case ConstraintKind::FunctionResult: case ConstraintKind::OpaqueUnderlyingType: case ConstraintKind::OneWayEqual: + case ConstraintKind::NominalEqual: + case ConstraintKind::PreferredDefault: assert(!First.isNull()); assert(!Second.isNull()); break; @@ -139,6 +141,8 @@ Constraint::Constraint(ConstraintKind Kind, Type First, Type Second, Type Third, case ConstraintKind::OpaqueUnderlyingType: case ConstraintKind::OneWayEqual: case ConstraintKind::DefaultClosureType: + case ConstraintKind::NominalEqual: + case ConstraintKind::PreferredDefault: llvm_unreachable("Wrong constructor"); case ConstraintKind::KeyPath: @@ -266,6 +270,8 @@ Constraint *Constraint::clone(ConstraintSystem &cs) const { case ConstraintKind::OpaqueUnderlyingType: case ConstraintKind::OneWayEqual: case ConstraintKind::DefaultClosureType: + case ConstraintKind::NominalEqual: + case ConstraintKind::PreferredDefault: return create(cs, getKind(), getFirstType(), getSecondType(), getLocator()); case ConstraintKind::BindOverload: @@ -351,6 +357,8 @@ void Constraint::print(llvm::raw_ostream &Out, SourceManager *sm) const { case ConstraintKind::DefaultClosureType: Out << " closure can default to "; break; + case ConstraintKind::NominalEqual: Out << " nominal equal to "; break; + case ConstraintKind::PreferredDefault: Out << " preferred default "; break; case ConstraintKind::KeyPath: Out << " key path from "; Out << getSecondType()->getString(PO); @@ -565,6 +573,8 @@ gatherReferencedTypeVars(Constraint *constraint, case ConstraintKind::OpaqueUnderlyingType: case ConstraintKind::OneWayEqual: case ConstraintKind::DefaultClosureType: + case ConstraintKind::NominalEqual: + case ConstraintKind::PreferredDefault: constraint->getFirstType()->getTypeVariables(typeVars); constraint->getSecondType()->getTypeVariables(typeVars); break; diff --git a/lib/Sema/Constraint.h b/lib/Sema/Constraint.h index 23e615fed081d..5f7ca87c47e41 100644 --- a/lib/Sema/Constraint.h +++ b/lib/Sema/Constraint.h @@ -170,6 +170,18 @@ enum class ConstraintKind : char { /// - Handled specially by binding inference, specifically contributes /// to the bindings only if there are no contextual types available. DefaultClosureType, + + /// The nominal type of the first type is equal to the nominal type of the + /// second, but any generic arguments may differ. Cannot be resolved until + /// at least the "outer layer" of the second type has been resolved to a + /// concrete type. After which it is treated like an equality constraint. + NominalEqual, + + /// The first type is defaultable to the second, but unlike \c Defaultable + /// constraints, this binding should be preferred over other alternatives. + /// This constraint functions more like an equality constraint except that it + /// isn't violated if + PreferredDefault, }; /// Classification of the different kinds of constraints. @@ -550,6 +562,8 @@ class Constraint final : public llvm::ilist_node, case ConstraintKind::OpaqueUnderlyingType: case ConstraintKind::OneWayEqual: case ConstraintKind::DefaultClosureType: + case ConstraintKind::NominalEqual: + case ConstraintKind::PreferredDefault: return ConstraintClassification::Relational; case ConstraintKind::ValueMember: diff --git a/lib/Sema/ConstraintLocator.cpp b/lib/Sema/ConstraintLocator.cpp index 3d866168d063e..b1489f00c2bf0 100644 --- a/lib/Sema/ConstraintLocator.cpp +++ b/lib/Sema/ConstraintLocator.cpp @@ -125,6 +125,7 @@ unsigned LocatorPathElt::getNewSummaryFlags() const { case ConstraintLocator::ImplicitCallAsFunction: case ConstraintLocator::TernaryBranch: case ConstraintLocator::PatternMatch: + case ConstraintLocator::UnresolvedMemberChainResult: return 0; case ConstraintLocator::FunctionArgument: @@ -500,6 +501,10 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const { case PatternMatch: out << "pattern match"; break; + + case UnresolvedMemberChainResult: + out << "unresolved chain result"; + break; } } out << ']'; diff --git a/lib/Sema/ConstraintLocatorPathElts.def b/lib/Sema/ConstraintLocatorPathElts.def index 3cb6923cf8265..c9d6133688b67 100644 --- a/lib/Sema/ConstraintLocatorPathElts.def +++ b/lib/Sema/ConstraintLocatorPathElts.def @@ -181,6 +181,9 @@ CUSTOM_LOCATOR_PATH_ELT(TernaryBranch) /// Performing a pattern patch. CUSTOM_LOCATOR_PATH_ELT(PatternMatch) +/// The result of a chain of member accesses off an UnresolvedMemberExpr +SIMPLE_LOCATOR_PATH_ELT(UnresolvedMemberChainResult) + #undef LOCATOR_PATH_ELT #undef CUSTOM_LOCATOR_PATH_ELT #undef SIMPLE_LOCATOR_PATH_ELT diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index db892b08f7668..ec4014b3deb04 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -3344,6 +3344,7 @@ void constraints::simplifyLocator(ASTNode &anchor, } case ConstraintLocator::ApplyFunction: + case ConstraintLocator::FunctionResult: // Extract application function. if (auto applyExpr = getAsExpr(anchor)) { anchor = applyExpr->getFn(); @@ -3511,6 +3512,11 @@ void constraints::simplifyLocator(ASTNode &anchor, continue; } + case ConstraintLocator::UnresolvedMemberChainResult: { + path = path.slice(1); + continue; + } + default: // FIXME: Lots of other cases to handle. break; diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index a68185f4825f8..ee6eec1d98b20 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -668,6 +668,8 @@ enum ScoreKind { SK_UserConversion, /// A non-trivial function conversion. SK_FunctionConversion, + /// A preferred default bound to something other than the preference. + SK_NonPreferredDefault, /// A literal expression bound to a non-default literal type. SK_NonDefaultLiteral, /// An implicit upcast conversion between collection types. @@ -919,6 +921,9 @@ class Solution { /// The node -> type mappings introduced by this solution. llvm::MapVector nodeTypes; + /// The unresolved member base types introduced by this solution. + llvm::MapVector unresolvedMemberBaseTypes; + /// Contextual types introduced by this solution. std::vector> contextualTypes; @@ -1029,6 +1034,9 @@ class Solution { /// Retrieve the type of the given node, as recorded in this solution. Type getType(ASTNode node) const; + /// Retrieve the type of a particular \c UnresolvedMemberExpr's base. + Type getUnresolvedMemberBaseType(UnresolvedMemberExpr *expr) const; + /// Resolve type variables present in the raw type, using generic parameter /// types where possible. Type resolveInterfaceType(Type type) const; @@ -1739,6 +1747,9 @@ class ConstraintSystem { llvm::DenseMap OpenedParameterTypes; + /// Maps \c UnresolvedMemberExprs to their (implicit) base types. + llvm::MapVector UnresolvedMemberBaseTypes; + /// The set of constraint restrictions used to reach the /// current constraint system. /// @@ -2264,6 +2275,8 @@ class ConstraintSystem { unsigned numAddedNodeTypes; + unsigned numUnresolvedMemberBaseTypes; + unsigned numCheckedConformances; unsigned numDisabledConstraints; @@ -2567,6 +2580,17 @@ class ConstraintSystem { return CTP_Unused; } + void setUnresolvedMemberBaseType(UnresolvedMemberExpr *expr, Type type) { + assert(expr && "Expected non-null expression"); + assert(type && "Expected non-null type"); + UnresolvedMemberBaseTypes[expr] = type; + } + + Type getUnresolvedMemberBaseType(UnresolvedMemberExpr *expr) { + assert(expr && "Expected non-null expression"); + return UnresolvedMemberBaseTypes.find(expr)->second; + } + void setSolutionApplicationTarget( SolutionApplicationTargetsKey key, SolutionApplicationTarget target) { assert(key && "Expected non-null solution application target key!"); @@ -2717,6 +2741,48 @@ class ConstraintSystem { bool isDeclUnavailable(const Decl *D, ConstraintLocator *locator = nullptr) const; + /// Find the next element in a chain of members. If this expression is (or + /// could be) the base of such a chain, this will return \c nullptr. + Expr *getMemberChainSubExpr(Expr *expr) { + assert(expr && "isMemberChainSubExpr called with null expr!"); + if (auto *UDE = dyn_cast(expr)) { + return UDE->getBase(); + } else if (auto *CE = dyn_cast(expr)) { + return CE->getFn(); + } else if (auto *BOE = dyn_cast(expr)) { + return BOE->getSubExpr(); + } else if (auto *FVE = dyn_cast(expr)) { + return FVE->getSubExpr(); + } else if (auto *SE = dyn_cast(expr)) { + return SE->getBase(); + } else { + return nullptr; + } + } + + /// Returns the base of a chain of member accesses/method calls. Most + /// expressions do not participate in member chains, and just return \c this. + Expr *getMemberChainBase(Expr *expr) { + if (auto *subExpr = getMemberChainSubExpr(expr)) + return getMemberChainBase(subExpr); + else + return expr; + } + + /// Whether this expression is a member of a member chain. + bool isMemberChainMember(Expr *expr) { + return getMemberChainSubExpr(expr) != nullptr; + } + /// Whether this expression sits at the end of a chain of member accesses. + bool isMemberChainTail(Expr *expr) { + assert(expr && "isMemberChainTail called with null expr!"); + // If this expression's parent is not itself part of a chain (or, this expr + // has no parent expr), this must be the tail of the chain. + Expr *parent = getParentExpr(expr); + + return parent == nullptr || !isMemberChainMember(parent); + } + public: /// Whether we should attempt to fix problems. @@ -4060,7 +4126,8 @@ class ConstraintSystem { ConstraintLocatorBuilder locator); /// Attempt to simplify the given defaultable constraint. - SolutionKind simplifyDefaultableConstraint(Type first, Type second, + SolutionKind simplifyDefaultableConstraint(ConstraintKind kind, Type first, + Type second, TypeMatchOptions flags, ConstraintLocatorBuilder locator); @@ -4076,6 +4143,11 @@ class ConstraintSystem { TypeMatchOptions flags, ConstraintLocatorBuilder locator); + SolutionKind simplifyNominalEqualConstraint(ConstraintKind kind, + Type first, Type second, + TypeMatchOptions flags, + ConstraintLocatorBuilder locator); + /// Simplify a conversion constraint by applying the given /// reduction rule, which is known to apply at the outermost level. SolutionKind simplifyRestrictedConstraintImpl( @@ -4198,6 +4270,12 @@ class ConstraintSystem { : nullptr; } + bool isPreferredDefaultBinding() const { + if (auto *constraint = BindingSource.dyn_cast()) + return constraint->getKind() == ConstraintKind::PreferredDefault; + return false; + } + ConstraintLocator *getLocator() const { if (auto *constraint = BindingSource.dyn_cast()) return constraint->getLocator(); @@ -4256,6 +4334,11 @@ class ConstraintSystem { /// Tracks the position of the last known supertype in the group. Optional lastSupertypeIndex; + /// Whether there are bindings that are not from a PreferredDefault + /// constraint. This allows us to avoid checking additional bindings in + /// most cases. + bool HasNonPreferredDefaultBindings = false; + /// A set of all constraints which contribute to pontential bindings. llvm::SmallPtrSet Sources; diff --git a/test/Constraints/construction.swift b/test/Constraints/construction.swift index ac8c185bf5145..c2936d4921a4d 100644 --- a/test/Constraints/construction.swift +++ b/test/Constraints/construction.swift @@ -22,7 +22,7 @@ enum Z { init(_ x: Int, _ y: Int) { self = .point(x, y) } } -enum Optional { // expected-note {{'T' declared as parameter to type 'Optional'}} +enum Optional { case none case value(T) @@ -59,8 +59,7 @@ acceptString("\(hello), \(world) #\(i)!") Optional(1) // expected-warning{{unused}} Optional(1) // expected-warning{{unused}} _ = .none as Optional -Optional(.none) // expected-error{{generic parameter 'T' could not be inferred}} expected-note {{explicitly specify the generic arguments to fix this issue}} {{9-9=}} -// expected-error@-1 {{cannot infer contextual base in reference to member 'none'}} +Optional(.none) // expected-error {{cannot infer contextual base in reference to member 'none'}} // Interpolation _ = "\(hello), \(world) #\(i)!" diff --git a/test/Constraints/diagnostics.swift b/test/Constraints/diagnostics.swift index 2e2fb2ab35813..db8c4b391cf5a 100644 --- a/test/Constraints/diagnostics.swift +++ b/test/Constraints/diagnostics.swift @@ -521,7 +521,7 @@ let _ : Color = .rainbow(42) // expected-error {{argument passed to call that t let _ : (Int, Float) = (42.0, 12) // expected-error {{cannot convert value of type '(Double, Float)' to specified type '(Int, Float)'}} -let _ : Color = .rainbow // expected-error {{member 'rainbow()' is a function; did you mean to call it?}} {{25-25=()}} +let _ : Color = .rainbow // expected-error {{member 'rainbow()' is a function that produces expected type 'Color'; did you mean to call it?}} {{25-25=()}} let _: Color = .overload(a : 1.0) // expected-error {{cannot convert value of type 'Double' to expected argument type 'Int'}} let _: Color = .overload(1.0) // expected-error {{no exact matches in call to static method 'overload'}} diff --git a/test/Constraints/one_way_solve.swift b/test/Constraints/one_way_solve.swift index 6f03fac0bc65d..f21a37e1e4137 100644 --- a/test/Constraints/one_way_solve.swift +++ b/test/Constraints/one_way_solve.swift @@ -38,9 +38,9 @@ func testTernaryOneWayOverload(b: Bool) { // CHECK: solving component #1 // CHECK: Initial bindings: $T11 := Int8 - // CHECK: found solution 0 0 0 0 0 0 0 2 0 0 0 0 0 + // CHECK: found solution 0 0 0 0 0 0 0 0 2 0 0 0 0 0 - // CHECK: composed solution 0 0 0 0 0 0 0 2 0 0 0 0 0 - // CHECK-NOT: composed solution 0 0 0 0 0 0 0 2 0 0 0 0 0 + // CHECK: composed solution 0 0 0 0 0 0 0 0 2 0 0 0 0 0 + // CHECK-NOT: composed solution 0 0 0 0 0 0 0 0 2 0 0 0 0 0 let _: Int8 = b ? Builtin.one_way(int8Or16(17)) : Builtin.one_way(int8Or16(42)) } diff --git a/test/expr/delayed-ident/member_chains.swift b/test/expr/delayed-ident/member_chains.swift new file mode 100644 index 0000000000000..b04de7a9bde65 --- /dev/null +++ b/test/expr/delayed-ident/member_chains.swift @@ -0,0 +1,159 @@ +// RUN: %target-typecheck-verify-swift -swift-version 5 + +struct ImplicitMembers: Equatable { + struct Other { + var implicit: ImplicitMembers { ImplicitMembers() } + } + + static var other = Other() + static func createOther() -> Other { + Other() + } + var anotherOther: Other { Other() } + func getAnotherOther() -> Other { + Other() + } + + static var implicit = ImplicitMembers() + static func createImplicit() -> ImplicitMembers { + ImplicitMembers() + } + + static var optional: ImplicitMembers? = ImplicitMembers() + static func createOptional() -> ImplicitMembers? { + ImplicitMembers() + } + static var superOptional: ImplicitMembers??? = ImplicitMembers() + + var another: ImplicitMembers { ImplicitMembers() } + + func getAnother() -> ImplicitMembers { + ImplicitMembers() + } + + func getAnother(arg: Int) -> ImplicitMembers { + ImplicitMembers() + } + + var anotherOptional: ImplicitMembers? { ImplicitMembers() } + + func getAnotherOptional() -> ImplicitMembers? { + ImplicitMembers() + } + + func getAnotherOptional(arg: Int) -> ImplicitMembers? { + ImplicitMembers() + } + + subscript(arg: Void) -> ImplicitMembers { ImplicitMembers() } + subscript(optional arg: Void) -> ImplicitMembers? { ImplicitMembers() } + subscript(func arg: Void) -> (() -> ImplicitMembers) { { ImplicitMembers() } } + subscript(funcOptional arg: Void) -> (() -> ImplicitMembers?) { { ImplicitMembers() } } + subscript(optionalFunc arg: Void) -> (() -> ImplicitMembers)? { { ImplicitMembers() } } +} + +let _: ImplicitMembers = .implicit +let _: ImplicitMembers? = .implicit +let _: ImplicitMembers? = .optional + +let _: ImplicitMembers = .implicit.another.another +let _: ImplicitMembers = .createImplicit().another.another +let _: ImplicitMembers = .init().another.another + +let _: ImplicitMembers = .implicit.getAnother().another +let _: ImplicitMembers = .createImplicit().getAnother().another +let _: ImplicitMembers = .init().getAnother().another + +let _: ImplicitMembers = .implicit.getAnother(arg: 0).another +let _: ImplicitMembers = .createImplicit().getAnother(arg: 0).another +let _: ImplicitMembers = .init().getAnother(arg: 0).another + +let _: ImplicitMembers = .implicit.another.getAnother() +let _: ImplicitMembers = .createImplicit().another.getAnother() +let _: ImplicitMembers = .init().another.getAnother() + +let _: ImplicitMembers = .implicit.another.getAnother(arg: 0) +let _: ImplicitMembers = .createImplicit().another.getAnother(arg: 0) +let _: ImplicitMembers = .init().another.getAnother(arg: 0) + +let _: ImplicitMembers = .implicit.getAnother().getAnother(arg: 0) +let _: ImplicitMembers = .createImplicit().getAnother().getAnother(arg: 0) +let _: ImplicitMembers = .init().getAnother().getAnother(arg: 0) + +let _: ImplicitMembers = .implicit.getAnother().getAnother(arg: 0).another +let _: ImplicitMembers = .createImplicit().getAnother().getAnother(arg: 0).another +let _: ImplicitMembers = .init().getAnother().getAnother(arg: 0).another + +let _: ImplicitMembers = .implicit.another.getAnother().getAnother(arg: 0) +let _: ImplicitMembers = .createImplicit().another.getAnother().getAnother(arg: 0) +let _: ImplicitMembers = .init().another.getAnother().getAnother(arg: 0) + +let _: ImplicitMembers = .implicit.another.another.another.another.another +let _: ImplicitMembers = .implicit.getAnother().getAnother().getAnother().getAnother().getAnother() +let _: ImplicitMembers = .implicit.getAnother(arg: 0).getAnother(arg: 0).getAnother(arg: 0).getAnother(arg: 0).getAnother(arg: 0) + +let _: ImplicitMembers = .optional! +let _: ImplicitMembers = .optional!.another +let _: ImplicitMembers = .createOptional()!.another +let _: ImplicitMembers = .optional!.anotherOptional! +let _: ImplicitMembers = .createOptional()!.anotherOptional! +let _: ImplicitMembers = .optional!.getAnotherOptional()! +let _: ImplicitMembers = .createOptional()!.getAnotherOptional()! + +let _: ImplicitMembers = .other.implicit +let _: ImplicitMembers = .implicit.anotherOther.implicit +let _: ImplicitMembers = .createOther().implicit +let _: ImplicitMembers = .implicit.getAnotherOther().implicit + +let _: ImplicitMembers = .other // expected-error {{member 'other' in 'ImplicitMembers' produces result of type 'ImplicitMembers.Other', but context expects 'ImplicitMembers'}} +let _: ImplicitMembers = .implicit.anotherOther // expected-error {{member 'implicit' in 'ImplicitMembers' produces result of type 'ImplicitMembers.Other', but context expects 'ImplicitMembers'}} +let _: ImplicitMembers = .implicit.anotherOther.nonDeclaredMember // expected-error {{value of type 'ImplicitMembers.Other' has no member 'nonDeclaredMember'}} +let _: ImplicitMembers = .implicit.anotherOther.nonDeclaredMethod() // expected-error {{value of type 'ImplicitMembers.Other' has no member 'nonDeclaredMethod'}} +let _: ImplicitMembers = .implicit.anotherOther.nonDeclaredMember.another // expected-error {{value of type 'ImplicitMembers.Other' has no member 'nonDeclaredMember'}} +let _: ImplicitMembers = .implicit.anotherOther.nonDeclaredMethod().another // expected-error {{value of type 'ImplicitMembers.Other' has no member 'nonDeclaredMethod'}} +let _: ImplicitMembers = .implicit.getAnotherOther() // expected-error {{member 'getAnotherOther()' in 'ImplicitMembers' produces result of type 'ImplicitMembers.Other', but context expects 'ImplicitMembers'}} + +let _: ImplicitMembers? = .implicit.another +let _: ImplicitMembers? = .implicit.anotherOptional + +let _: ImplicitMembers? = .optional +let _: ImplicitMembers? = .optional?.another +let _: ImplicitMembers? = .optional?.anotherOptional +let _: ImplicitMembers? = .optional?.getAnother() +let _: ImplicitMembers? = .optional?.getAnotherOptional() +let _: ImplicitMembers? = .optional?.anotherOptional?.another +let _: ImplicitMembers? = .optional?.getAnotherOptional()?.another +let _: ImplicitMembers? = .createOptional() +let _: ImplicitMembers? = .createOptional()?.another +let _: ImplicitMembers? = .createOptional()?.anotherOptional +let _: ImplicitMembers? = .createOptional()?.getAnother() +let _: ImplicitMembers? = .createOptional()?.getAnotherOptional() +let _: ImplicitMembers? = .createOptional()?.anotherOptional?.another +let _: ImplicitMembers? = .createOptional()?.getAnotherOptional()?.another +// FIXME: This should be allowed +// let _: ImplicitMembers? = .superOptional???.another + +let _: ImplicitMembers = .implicit[()] +let _: ImplicitMembers = .implicit[optional: ()]! +let _: ImplicitMembers? = .implicit[optional: ()] +let _: ImplicitMembers = .implicit[func: ()]() +let _: ImplicitMembers = .implicit[funcOptional: ()]()! +let _: ImplicitMembers? = .implicit[funcOptional: ()]() +let _: ImplicitMembers = .implicit[optionalFunc: ()]!() +let _: ImplicitMembers? = .implicit[optionalFunc: ()]?() +let _: ImplicitMembers = .implicit.another[()] +let _: ImplicitMembers = .implicit.another[optional: ()]! +let _: ImplicitMembers? = .implicit.another[optional: ()] +let _: ImplicitMembers = .implicit.another[func: ()]() +let _: ImplicitMembers = .implicit.another[funcOptional: ()]()! +let _: ImplicitMembers? = .implicit.another[funcOptional: ()]() +let _: ImplicitMembers = .implicit.another[optionalFunc: ()]!() +let _: ImplicitMembers? = .implicit.another[optionalFunc: ()]?() +let _: ImplicitMembers = .implicit[()].another +let _: ImplicitMembers = .implicit[optional: ()]!.another +let _: ImplicitMembers? = .implicit[optional: ()]?.another +let _: ImplicitMembers = .implicit[func: ()]().another +let _: ImplicitMembers = .implicit[funcOptional: ()]()!.another +let _: ImplicitMembers? = .implicit[funcOptional: ()]()?.another +let _: ImplicitMembers = .implicit[optionalFunc: ()]!().another +let _: ImplicitMembers? = .implicit[optionalFunc: ()]?().another diff --git a/test/expr/delayed-ident/member_chains_generic.swift b/test/expr/delayed-ident/member_chains_generic.swift new file mode 100644 index 0000000000000..261ac659a1c8c --- /dev/null +++ b/test/expr/delayed-ident/member_chains_generic.swift @@ -0,0 +1,78 @@ +// RUN: %target-typecheck-verify-swift -swift-version 5 + +struct ImplicitGeneric { + static var implicit: ImplicitGeneric { ImplicitGeneric() } + var another: ImplicitGeneric { ImplicitGeneric() } + func getAnother() -> ImplicitGeneric { + ImplicitGeneric() + } + static var implicitGenericInt: ImplicitGeneric { ImplicitGeneric() } +} + +extension ImplicitGeneric where T == Int { + static var implicitInt: ImplicitGeneric { ImplicitGeneric() } + static var implicitString: ImplicitGeneric { ImplicitGeneric() } + var anotherInt: ImplicitGeneric { ImplicitGeneric() } + var anotherIntString: ImplicitGeneric { ImplicitGeneric() } + func getAnotherInt() -> ImplicitGeneric { + ImplicitGeneric() + } +} + +extension ImplicitGeneric where T == String { + static var implicitString: ImplicitGeneric { ImplicitGeneric() } + var anotherString: ImplicitGeneric { ImplicitGeneric() } + var anotherStringInt: ImplicitGeneric { ImplicitGeneric() } + func getAnotherString() -> ImplicitGeneric { + ImplicitGeneric() + } + func getAnotherStringInt() -> ImplicitGeneric { + ImplicitGeneric() + } +} + +func implicit(_ arg: ImplicitGeneric) {} + +implicit(.implicitInt) +implicit(.implicitGenericInt) +implicit(.implicit.anotherInt) +implicit(.implicit.anotherInt.another) +implicit(.implicit.another.anotherInt) +implicit(.implicit.getAnotherInt()) +implicit(.implicit.another.getAnotherInt()) +implicit(.implicit.getAnother().anotherInt) +implicit(.implicit.getAnotherInt()) +implicit(.implicit.getAnother().getAnotherInt()) +implicit(.implicitString.anotherStringInt) +let _: ImplicitGeneric = .implicitString.anotherStringInt +// Member types along the chain can have different generic arguments +implicit(.implicit.anotherIntString.anotherStringInt) + +implicit(.implicit.anotherString.anotherStringInt) +implicit(.implicit.getAnotherString().anotherStringInt) +implicit(.implicit.anotherString.getAnotherStringInt()) +implicit(.implicit.getAnotherString().getAnotherStringInt()) + +struct SomeError: Error { + var code: Int +} + +func process(_ callback: (Result) -> ()) { + callback(.success(0).map { "\($0)" } .mapError { SomeError(code: $0.code + 1) }) +} + +func process2(_ callback: (Result) -> ()) { + callback(.success(0).map { "\($0)" }) +} + +let _: Result = .success(0).map { "\($0)" } + +struct Adder { + init(x: T, y: T) {} + static func add(x: T, y: T) -> Adder { + print(x + y) + return Adder(x: .zero, y: .zero) + } +} + +let _: Adder = .add(x: 128, y: 128) diff --git a/test/expr/delayed-ident/nested_type.swift b/test/expr/delayed-ident/nested_type.swift index da8a3fece6538..520d08c90dea9 100644 --- a/test/expr/delayed-ident/nested_type.swift +++ b/test/expr/delayed-ident/nested_type.swift @@ -4,12 +4,21 @@ class Base { class Derived : Base { init(x: Int) {} + class Sub: Derived { + init(y: Int) {} + } + typealias Ident = Derived + typealias Ident2 = Base } typealias Ident = Base } let _: Base = .Derived(x: 12) let _: Base = .Ident() +let _: Base = .Derived.Sub(y: 1) +let _: Base = .Derived.init(x: 3) +let _: Base = .Derived.Ident(x: 3) +let _: Base = .Derived.Ident2() // Typealias in protocol. protocol P { @@ -19,9 +28,19 @@ extension P { typealias Impl2 = ConcreteP } struct ConcreteP : P { + struct NestedP: P {} + typealias Same = ConcreteP } let _: P = .Impl1() let _: P = .Impl2() let _: ConcreteP = .Impl1() let _: ConcreteP = .Impl2() +let _: P = .Impl1.NestedP() +let _: P = .Impl2.NestedP() +let _: ConcreteP = .Impl1.Same() +let _: ConcreteP = .Impl2.Same() +let _: P = .Impl1.init() +let _: P = .Impl2.init() +let _: ConcreteP = .Impl1.init() +let _: ConcreteP = .Impl2.init() diff --git a/test/expr/delayed-ident/static_var.swift b/test/expr/delayed-ident/static_var.swift index f6b28bf5569c5..b8864a6872cb4 100644 --- a/test/expr/delayed-ident/static_var.swift +++ b/test/expr/delayed-ident/static_var.swift @@ -50,5 +50,4 @@ var _: HasClosure = .factoryOpt(3) // expected-error@-1 {{value of optional type '((Int) -> HasClosure)?' must be unwrapped to a value of type '(Int) -> HasClosure'}} // expected-note@-2 {{coalesce}} // expected-note@-3 {{force-unwrap}} -// FIXME: we should accept this -var _: HasClosure = .factoryOpt!(4) // expected-error {{cannot infer contextual base in reference to member 'factoryOpt'}} +var _: HasClosure = .factoryOpt!(4) diff --git a/test/expr/postfix/dot/optional_context_member.swift b/test/expr/postfix/dot/optional_context_member.swift index b761c21657ab6..c3279781e40b0 100644 --- a/test/expr/postfix/dot/optional_context_member.swift +++ b/test/expr/postfix/dot/optional_context_member.swift @@ -27,9 +27,8 @@ func nonOptContext() -> Foo { // expected-error@-1 {{value of optional type 'Foo?' must be unwrapped to a value of type 'Foo'}} // expected-note@-2 {{coalesce}} // expected-note@-3 {{force-unwrap}} - // TODO - //case (): - // return .someOptFunc()! + case (): // expected-warning {{case is already handled by previous patterns; consider removing it}} + return .someOptFunc()! } } diff --git a/validation-test/Sema/rdar30933988.swift b/validation-test/Sema/rdar30933988.swift index baeeb46ef1054..2e68281eb1d1e 100644 --- a/validation-test/Sema/rdar30933988.swift +++ b/validation-test/Sema/rdar30933988.swift @@ -9,5 +9,5 @@ let _: E = .foo let _: E = .foo // Ok let _: E = .bar(42) // Ok let _: E = .bar(42) -// expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'String'}} +// expected-error@-1 {{member 'bar' in 'E' produces result of type 'E', but context expects 'E'}} let _: E = .bar(42) // Ok