//===--- ConstraintLocator.cpp - Constraint Locator -----------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2018 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 the \c ConstraintLocator class and its related types, // which is used by the constraint-based type checker to describe how // a particular constraint was derived. // //===----------------------------------------------------------------------===// #include "swift/Sema/ConstraintLocator.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" #include "swift/AST/Types.h" #include "swift/AST/ProtocolConformance.h" #include "swift/Sema/Constraint.h" #include "swift/Sema/ConstraintLocator.h" #include "swift/Sema/ConstraintSystem.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/raw_ostream.h" using namespace swift; using namespace constraints; void ConstraintLocator::Profile(llvm::FoldingSetNodeID &id, ASTNode anchor, ArrayRef path) { id.AddPointer(anchor.getOpaqueValue()); id.AddInteger(path.size()); for (auto elt : path) { id.AddInteger(elt.getKind()); id.AddInteger(elt.getRawStorage()); } } unsigned LocatorPathElt::getNewSummaryFlags() const { switch (getKind()) { case ConstraintLocator::ApplyArgument: case ConstraintLocator::ApplyFunction: case ConstraintLocator::SequenceElementType: case ConstraintLocator::ClosureResult: case ConstraintLocator::ClosureThrownError: case ConstraintLocator::ClosureBody: case ConstraintLocator::ConstructorMember: case ConstraintLocator::ConstructorMemberType: case ConstraintLocator::ResultBuilderBodyResult: case ConstraintLocator::InstanceType: case ConstraintLocator::AutoclosureResult: case ConstraintLocator::OptionalPayload: case ConstraintLocator::Member: case ConstraintLocator::MemberRefBase: case ConstraintLocator::UnresolvedMember: case ConstraintLocator::ParentType: case ConstraintLocator::ExistentialConstraintType: case ConstraintLocator::ProtocolCompositionMemberType: case ConstraintLocator::LValueConversion: case ConstraintLocator::DynamicType: case ConstraintLocator::SubscriptMember: case ConstraintLocator::OpenedGeneric: case ConstraintLocator::OpenedOpaqueArchetype: case ConstraintLocator::WrappedValue: case ConstraintLocator::GenericParameter: case ConstraintLocator::GenericArgument: case ConstraintLocator::TupleType: case ConstraintLocator::GenericType: case ConstraintLocator::NamedTupleElement: case ConstraintLocator::TupleElement: case ConstraintLocator::ProtocolRequirement: case ConstraintLocator::Witness: case ConstraintLocator::KeyPathComponent: case ConstraintLocator::ConditionalRequirement: case ConstraintLocator::TypeParameterRequirement: case ConstraintLocator::ConformanceRequirement: case ConstraintLocator::ImplicitlyUnwrappedDisjunctionChoice: case ConstraintLocator::DynamicLookupResult: case ConstraintLocator::ContextualType: case ConstraintLocator::SynthesizedArgument: case ConstraintLocator::KeyPathDynamicMember: case ConstraintLocator::KeyPathType: case ConstraintLocator::KeyPathRoot: case ConstraintLocator::KeyPathValue: case ConstraintLocator::KeyPathComponentResult: case ConstraintLocator::Condition: case ConstraintLocator::DynamicCallable: case ConstraintLocator::ImplicitCallAsFunction: case ConstraintLocator::TernaryBranch: case ConstraintLocator::PatternMatch: case ConstraintLocator::EnumPatternImplicitCastMatch: case ConstraintLocator::ArgumentAttribute: case ConstraintLocator::UnresolvedMemberChainResult: case ConstraintLocator::PlaceholderType: case ConstraintLocator::ImplicitConversion: case ConstraintLocator::ImplicitDynamicMemberSubscript: case ConstraintLocator::SyntacticElement: case ConstraintLocator::PackType: case ConstraintLocator::PackElement: case ConstraintLocator::PackShape: case ConstraintLocator::PackExpansionPattern: case ConstraintLocator::PatternBindingElement: case ConstraintLocator::NamedPatternDecl: case ConstraintLocator::SingleValueStmtResult: case ConstraintLocator::AnyPatternDecl: case ConstraintLocator::GlobalActorType: case ConstraintLocator::CoercionOperand: case ConstraintLocator::PackExpansionType: case ConstraintLocator::ThrownErrorType: case ConstraintLocator::FallbackType: case ConstraintLocator::KeyPathSubscriptIndex: case ConstraintLocator::ExistentialMemberAccessConversion: return 0; case ConstraintLocator::FunctionArgument: case ConstraintLocator::FunctionResult: return IsFunctionConversion; case ConstraintLocator::ApplyArgToParam: { auto flags = castTo().getParameterFlags(); return flags.isNonEphemeral() ? IsNonEphemeralParam : 0; } } llvm_unreachable("Unhandled PathElementKind in switch."); } void LocatorPathElt::dump(raw_ostream &out) const { PrintOptions PO; PO.PrintTypesForDebugging = true; auto dumpReqKind = [&out](RequirementKind kind) { out << " ("; switch (kind) { case RequirementKind::SameShape: out << "same_shape"; break; case RequirementKind::Conformance: out << "conformance"; break; case RequirementKind::Superclass: out << "superclass"; break; case RequirementKind::SameType: out << "same-type"; break; case RequirementKind::Layout: out << "layout"; break; } out << ")"; }; const LocatorPathElt &elt = *this; switch (getKind()) { case ConstraintLocator::GenericParameter: { auto gpElt = elt.castTo(); out << "generic parameter '" << gpElt.getType()->getString(PO) << "'"; break; } case ConstraintLocator::WrappedValue: { auto wrappedValueElt = elt.castTo(); out << "composed property wrapper type '" << wrappedValueElt.getType()->getString(PO) << "'"; break; } case ConstraintLocator::ApplyArgument: out << "apply argument"; break; case ConstraintLocator::ApplyFunction: out << "apply function"; break; case ConstraintLocator::OptionalPayload: out << "optional payload"; break; case ConstraintLocator::ApplyArgToParam: { auto argElt = elt.castTo(); out << "comparing call argument #" << llvm::utostr(argElt.getArgIdx()) << " to parameter #" << llvm::utostr(argElt.getParamIdx()); if (argElt.getParameterFlags().isNonEphemeral()) out << " (non-ephemeral)"; break; } case ConstraintLocator::ClosureResult: out << "closure result"; break; case ConstraintLocator::ClosureThrownError: out << "closure thrown error"; break; case ConstraintLocator::ClosureBody: out << "type of a closure body"; break; case ConstraintLocator::ConstructorMember: out << "constructor member"; break; case ConstraintLocator::ConstructorMemberType: { auto memberTypeElt = elt.castTo(); out << "constructor member type"; if (memberTypeElt.isShortFormOrSelfDelegatingConstructor()) out << " (for short-form or self.init call)"; break; } case ConstraintLocator::FunctionArgument: out << "function argument"; break; case ConstraintLocator::FunctionResult: out << "function result"; break; case ConstraintLocator::ResultBuilderBodyResult: out << "result builder body result"; break; case ConstraintLocator::SequenceElementType: out << "sequence element type"; break; case ConstraintLocator::GenericArgument: { auto genericElt = elt.castTo(); out << "generic argument #" << llvm::utostr(genericElt.getIndex()); break; } case ConstraintLocator::InstanceType: out << "instance type"; break; case ConstraintLocator::AutoclosureResult: out << "@autoclosure result"; break; case ConstraintLocator::Member: out << "member"; break; case ConstraintLocator::MemberRefBase: out << "member reference base"; break; case ConstraintLocator::TupleType: { auto tupleElt = elt.castTo(); out << "tuple type '" << tupleElt.getType()->getString(PO) << "'"; break; } case ConstraintLocator::GenericType: { auto genericTyElt = elt.castTo(); out << "generic type '" << genericTyElt.getType()->getString(PO) << "'"; break; } case ConstraintLocator::NamedTupleElement: { auto tupleElt = elt.castTo(); out << "named tuple element #" << llvm::utostr(tupleElt.getIndex()); break; } case ConstraintLocator::UnresolvedMember: out << "unresolved member"; break; case ConstraintLocator::ParentType: out << "parent type"; break; case ConstraintLocator::ExistentialConstraintType: out << "existential constraint type"; break; case ConstraintLocator::ProtocolCompositionMemberType: { auto memberElt = elt.castTo(); out << "protocol composition member " << llvm::utostr(memberElt.getIndex()); break; } case ConstraintLocator::LValueConversion: out << "@lvalue-to-inout conversion"; break; case ConstraintLocator::DynamicType: out << "`.dynamicType` reference"; break; case ConstraintLocator::SubscriptMember: out << "subscript member"; break; case ConstraintLocator::TupleElement: { auto tupleElt = elt.castTo(); out << "tuple element #" << llvm::utostr(tupleElt.getIndex()); break; } case ConstraintLocator::KeyPathComponent: { auto kpElt = elt.castTo(); out << "key path component #" << llvm::utostr(kpElt.getIndex()); break; } case ConstraintLocator::ProtocolRequirement: { out << "protocol requirement"; break; } case ConstraintLocator::Witness: { auto witnessElt = elt.castTo(); out << "witness "; witnessElt.getDecl()->dumpRef(out); break; } case ConstraintLocator::OpenedGeneric: out << "opened generic"; break; case ConstraintLocator::OpenedOpaqueArchetype: out << "opened opaque archetype"; break; case ConstraintLocator::ConditionalRequirement: { auto reqElt = elt.castTo(); out << "conditional requirement #" << llvm::utostr(reqElt.getIndex()); dumpReqKind(reqElt.getRequirementKind()); break; } case ConstraintLocator::TypeParameterRequirement: { auto reqElt = elt.castTo(); out << "type parameter requirement #" << llvm::utostr(reqElt.getIndex()); dumpReqKind(reqElt.getRequirementKind()); break; } case ConstraintLocator::ConformanceRequirement: { auto *conformance = elt.castTo().getConformance(); out << "conformance requirement ("; conformance->getProtocol()->dumpRef(out); out << ")"; break; } case ConstraintLocator::ImplicitlyUnwrappedDisjunctionChoice: out << "implicitly unwrapped disjunction choice"; break; case ConstraintLocator::DynamicLookupResult: out << "dynamic lookup result"; break; case ConstraintLocator::ContextualType: out << "contextual type"; break; case ConstraintLocator::SynthesizedArgument: { auto argElt = elt.castTo(); out << "synthesized argument #" << llvm::utostr(argElt.getIndex()); break; } case ConstraintLocator::KeyPathDynamicMember: out << "key path dynamic member lookup"; break; case ConstraintLocator::KeyPathType: out << "key path type"; break; case ConstraintLocator::KeyPathRoot: out << "key path root"; break; case ConstraintLocator::KeyPathValue: out << "key path value"; break; case ConstraintLocator::KeyPathComponentResult: out << "key path component result"; break; case ConstraintLocator::Condition: out << "condition expression"; break; case ConstraintLocator::DynamicCallable: out << "implicit call to @dynamicCallable method"; break; case ConstraintLocator::ImplicitCallAsFunction: out << "implicit reference to callAsFunction"; break; case ConstraintLocator::TernaryBranch: { auto branchElt = elt.castTo(); out << (branchElt.forThen() ? "'then'" : "'else'") << " branch of a ternary operator"; break; } case ConstraintLocator::SingleValueStmtResult: { auto branch = elt.castTo(); out << "result [" << branch.getIndex() << "] of " "single value stmt"; break; } case ConstraintLocator::PatternMatch: out << "pattern match"; break; case ConstraintLocator::EnumPatternImplicitCastMatch: out << "enum pattern implicit cast match"; break; case ConstraintLocator::ArgumentAttribute: { using AttrLoc = LocatorPathElt::ArgumentAttribute; auto attrElt = elt.castTo(); out << "argument attribute: "; switch (attrElt.getAttr()) { case AttrLoc::Attribute::InOut: out << "inout"; break; case AttrLoc::Attribute::Escaping: out << "@escaping"; break; case AttrLoc::Attribute::Concurrent: out << "@Sendable"; break; case AttrLoc::Attribute::GlobalActor: out << "@"; break; } break; } case ConstraintLocator::UnresolvedMemberChainResult: out << "unresolved chain result"; break; case ConstraintLocator::PlaceholderType: out << "placeholder type"; break; case ConstraintLocator::ConstraintLocator::SyntacticElement: // TODO: Would be great to print a kind of element this is e.g. // "if", "for each", "switch" etc. out << "syntactic element"; break; case ConstraintLocator::ConstraintLocator::ImplicitDynamicMemberSubscript: out << "implicit dynamic member subscript"; break; case ConstraintLocator::ConstraintLocator::ImplicitConversion: { auto convElt = elt.castTo(); out << "implicit conversion " << getName(convElt.getConversionKind()); break; } case ConstraintLocator::ConstraintLocator::PackType: { auto packElt = elt.castTo(); out << "pack type '" << packElt.getType()->getString(PO) << "'"; break; } case ConstraintLocator::PackElement: { auto packElt = elt.castTo(); out << "pack element #" << llvm::utostr(packElt.getIndex()); break; } case ConstraintLocator::PackShape: { out << "pack shape"; break; } case ConstraintLocator::PackExpansionPattern: { out << "pack expansion pattern"; break; } case ConstraintLocator::PatternBindingElement: { auto patternBindingElt = elt.castTo(); out << "pattern binding element #" << llvm::utostr(patternBindingElt.getIndex()); break; } case ConstraintLocator::NamedPatternDecl: { out << "named pattern decl"; break; } case ConstraintLocator::AnyPatternDecl: { out << "'_' pattern decl"; break; } case ConstraintLocator::GlobalActorType: { out << "global actor type"; break; } case ConstraintLocator::CoercionOperand: { out << "coercion operand"; break; case ConstraintLocator::PackExpansionType: auto expansionElt = elt.castTo(); out << "pack expansion type (" << expansionElt.getOpenedType()->getString(PO) << ")"; break; } case ConstraintLocator::ThrownErrorType: { out << "thrown error type"; break; } case ConstraintLocator::FallbackType: { out << "fallback type"; break; case ConstraintLocator::KeyPathSubscriptIndex: out << "key path subscript index parameter"; break; } case ConstraintLocator::ExistentialMemberAccessConversion: out << "existential member access conversion"; break; } } /// Determine whether given locator points to the subscript reference /// e.g. `foo[0]` or `\Foo.[0]` bool ConstraintLocator::isSubscriptMemberRef() const { auto anchor = getAnchor(); auto path = getPath(); if (!anchor || path.empty()) return false; return path.back().getKind() == ConstraintLocator::SubscriptMember; } bool ConstraintLocator::isKeyPathType() const { auto anchor = getAnchor(); auto path = getPath(); // The format of locator should be ` -> key path type` if (!anchor || !isExpr(anchor) || path.size() != 1) return false; return path.back().is(); } bool ConstraintLocator::isKeyPathRoot() const { auto anchor = getAnchor(); auto path = getPath(); if (!anchor || path.empty()) return false; return path.back().getKind() == ConstraintLocator::KeyPathRoot; } bool ConstraintLocator::isKeyPathValue() const { auto anchor = getAnchor(); auto path = getPath(); if (!anchor || path.empty()) return false; return path.back().getKind() == ConstraintLocator::KeyPathValue; } bool ConstraintLocator::isResultOfKeyPathDynamicMemberLookup() const { return llvm::any_of(getPath(), [](const LocatorPathElt &elt) { return elt.isKeyPathDynamicMember(); }); } bool ConstraintLocator::isKeyPathSubscriptComponent() const { auto *anchor = getAsExpr(getAnchor()); auto *KPE = dyn_cast_or_null(anchor); if (!KPE) return false; using ComponentKind = KeyPathExpr::Component::Kind; return llvm::any_of(getPath(), [&](const LocatorPathElt &elt) { auto keyPathElt = elt.getAs(); if (!keyPathElt) return false; auto index = keyPathElt->getIndex(); auto &component = KPE->getComponents()[index]; return component.getKind() == ComponentKind::Subscript || component.getKind() == ComponentKind::UnresolvedSubscript; }); } bool ConstraintLocator::isForKeyPathDynamicMemberLookup() const { auto path = getPath(); return !path.empty() && path.back().isKeyPathDynamicMember(); } bool ConstraintLocator::isInKeyPathComponent() const { return llvm::any_of(getPath(), [&](const LocatorPathElt &elt) { return elt.isKeyPathComponent(); }); } bool ConstraintLocator::isForKeyPathComponentResult() const { return isLastElement(); } bool ConstraintLocator::isForKeyPathComponent() const { return isLastElement(); } bool ConstraintLocator::isForGenericParameter() const { return isLastElement(); } bool ConstraintLocator::isForWrappedValue() const { return isLastElement(); } bool ConstraintLocator::isForSequenceElementType() const { return isLastElement(); } bool ConstraintLocator::isForContextualType() const { return isLastElement(); } bool ConstraintLocator::isForContextualType(ContextualTypePurpose ctp) const { auto elt = getLastElementAs(); return elt && elt->getPurpose() == ctp; } bool ConstraintLocator::isForAssignment() const { return directlyAt(); } bool ConstraintLocator::isForCoercion() const { return isLastElement(); } bool ConstraintLocator::isForOptionalTry() const { return directlyAt(); } bool ConstraintLocator::isForResultBuilderBodyResult() const { return isFirstElement(); } bool ConstraintLocator::isForMacroExpansion() const { return directlyAt(); } static bool isForSingleValueStmtConjunction(ASTNode anchor, ArrayRef path) { auto *SVE = getAsExpr(anchor); if (!SVE) return false; if (!path.empty()) { // Ignore a trailing SyntacticElement path element for the statement. if (auto elt = path.back().getAs()) { if (elt->getElement() == ASTNode(SVE->getStmt())) path = path.drop_back(); } } // Other than the trailing SyntaticElement, we must be at the anchor. return path.empty(); } bool ConstraintLocator::isForSingleValueStmtConjunction() const { return ::isForSingleValueStmtConjunction(getAnchor(), getPath()); } bool ConstraintLocator::isForSingleValueStmtConjunctionOrBrace() const { auto *SVE = getAsExpr(getAnchor()); if (!SVE) return false; auto path = getPath(); while (!path.empty()) { // Ignore a trailing TernaryBranch locator, which is used for if statements. if (path.back().is()) { path = path.drop_back(); continue; } // Ignore a SyntaticElement path element for a case statement of a switch, // or the catch of a do-catch, or the brace of a do-statement. if (auto elt = path.back().getAs()) { if (elt->getElement().isStmt(StmtKind::Case)) { path = path.drop_back(); break; } if (elt->getElement().isStmt(StmtKind::Brace) && isa(SVE->getStmt())) { path = path.drop_back(); break; } } break; } return ::isForSingleValueStmtConjunction(getAnchor(), path); } bool ConstraintLocator::isForSingleValueStmtBranch() const { if (!isExpr(getAnchor())) return false; // Ignore a trailing ContextualType path element. auto path = getPath(); if (isLastElement()) path = path.drop_back(); if (path.empty()) return false; return path.back().is(); } NullablePtr ConstraintLocator::getPatternMatch() const { auto matchElt = findLast(); if (!matchElt) return nullptr; return matchElt->getPattern(); } bool ConstraintLocator::isForPatternMatch() const { return getPatternMatch() != nullptr; } bool ConstraintLocator::isMemberRef() const { if (isLastElement()) { return true; } else if (isLastElement()) { auto path = getPath(); if (path.size() >= 2 && path[path.size() - 2].is()) { return true; } } return false; } GenericTypeParamType *ConstraintLocator::getGenericParameter() const { // Check whether we have a path that terminates at a generic parameter. return isForGenericParameter() ? castLastElementTo().getType() : nullptr; } Type ConstraintLocator::getWrappedValue() const { return isForWrappedValue() ? castLastElementTo().getType() : Type(); } void ConstraintLocator::dump(SourceManager *sm) const { dump(sm, llvm::errs()); llvm::errs() << "\n"; } void ConstraintLocator::dump(ConstraintSystem *CS) const { dump(&CS->getASTContext().SourceMgr, llvm::errs()); llvm::errs() << "\n"; } void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const { PrintOptions PO; PO.PrintTypesForDebugging = true; out << "locator@" << (void*) this << " ["; constraints::dumpAnchor(anchor, sm, out); for (auto elt : getPath()) { out << " → "; elt.dump(out); } out << ']'; } void ConstraintLocatorBuilder::dump(SourceManager *sm) const { dump(sm, llvm::errs()); llvm::errs() << "\n"; } void ConstraintLocatorBuilder::dump(ConstraintSystem *CS) const { dump(&CS->getASTContext().SourceMgr, llvm::errs()); llvm::errs() << "\n"; } void ConstraintLocatorBuilder::dump(SourceManager *SM, llvm::raw_ostream &out) const { if (auto prev = previous.dyn_cast()) { prev->dump(SM, out); } else if (auto prev = previous.dyn_cast()) { prev->dump(SM, out); } if (element) { out << " → "; element->dump(out); } }