//===--- CSDiagnostics.cpp - Constraint Diagnostics -----------------------===// // // 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 diagnostics for constraint system. // //===----------------------------------------------------------------------===// #include "ConstraintSystem.h" #include "CSDiagnostics.h" #include "MiscDiagnostics.h" #include "swift/AST/Expr.h" #include "swift/AST/Types.h" #include "llvm/ADT/ArrayRef.h" using namespace swift; using namespace constraints; FailureDiagnostic::~FailureDiagnostic() {} Type FailureDiagnostic::getType(Expr *expr) const { auto &cs = getConstraintSystem(); return solution.simplifyType(cs.getType(expr)); } template InFlightDiagnostic FailureDiagnostic::emitDiagnostic(ArgTypes &&... Args) const { auto &cs = getConstraintSystem(); return cs.TC.diagnose(std::forward(Args)...); } Type RequirementFailure::getOwnerType() const { return getType(getAnchor())->getRValueInstanceType(); } const Requirement &RequirementFailure::getRequirement() { auto *genericCtx = AffectedDecl->getAsGenericContext(); return genericCtx->getGenericRequirements()[getRequirementIndex()]; } ValueDecl *RequirementFailure::getDeclRef() const { auto &cs = getConstraintSystem(); auto *anchor = getAnchor(); auto *locator = cs.getConstraintLocator(anchor); if (auto *AE = dyn_cast(anchor)) { assert(isa(AE->getFn())); ConstraintLocatorBuilder ctor(locator); locator = cs.getConstraintLocator( ctor.withPathElement(PathEltKind::ApplyFunction) .withPathElement(PathEltKind::ConstructorMember)); } else if (auto *UDE = dyn_cast(anchor)) { ConstraintLocatorBuilder member(locator); locator = cs.getConstraintLocator(member.withPathElement(PathEltKind::Member)); } auto overload = getOverloadChoiceIfAvailable(locator); if (overload) return overload->choice.getDecl(); auto ownerType = getOwnerType(); if (auto *NA = dyn_cast(ownerType.getPointer())) return NA->getDecl(); return ownerType->getAnyGeneric(); } bool MissingConformanceFailure::diagnose() { auto *anchor = getAnchor(); auto ownerType = getOwnerType(); auto type = getNonConformingType(); auto protocolType = getProtocolType(); // Find `ApplyExpr` based on a function expression attached to it. auto findApplyExpr = [](Expr *parent, Expr *fnExpr) -> ApplyExpr * { ApplyExpr *applyExpr = nullptr; parent->forEachChildExpr([&applyExpr, &fnExpr](Expr *subExpr) -> Expr * { auto *AE = dyn_cast(subExpr); if (!AE || AE->getFn() != fnExpr) return subExpr; applyExpr = AE; return nullptr; }); return applyExpr; }; auto getArgumentAt = [](ApplyExpr *AE, unsigned index) -> Expr * { assert(AE); auto *arg = AE->getArg(); if (auto *TE = dyn_cast(arg)) return TE->getElement(index); assert(index == 0); if (auto *PE = dyn_cast(arg)) return PE->getSubExpr(); return arg; }; auto *applyExpr = findApplyExpr(getParentExpr(), anchor); Optional atParameterPos; // Sometimes fix is recorded by type-checking sub-expression // during normal diagnostics, in such case call expression // is unavailable. if (applyExpr) { // If this is a static, initializer or operator call, // let's not try to diagnose it here, but refer to expression // diagnostics. if (isa(applyExpr) || isa(anchor)) return false; if (auto *fnType = ownerType->getAs()) { auto parameters = fnType->getParams(); for (auto index : indices(parameters)) { if (parameters[index].getType()->isEqual(type)) { atParameterPos = index; break; } } } } if (type->isExistentialType()) { auto diagnostic = diag::protocol_does_not_conform_objc; if (type->isObjCExistentialType()) diagnostic = diag::protocol_does_not_conform_static; emitDiagnostic(anchor->getLoc(), diagnostic, type, protocolType); } else if (atParameterPos) { // Requirement comes from one of the parameter types, // let's try to point diagnostic to the argument expression. auto *argExpr = getArgumentAt(applyExpr, *atParameterPos); emitDiagnostic(argExpr->getLoc(), diag::cannot_convert_argument_value_protocol, type, protocolType); } else { const auto &req = getRequirement(); auto *genericCtx = AffectedDecl->getAsGenericContext(); std::function getAffectedCtx = [&](Type type) -> const DeclContext * { if (auto *DMT = type->getAs()) return getAffectedCtx(DMT->getBase()); if (auto *GPT = type->getAs()) return GPT->getDecl()->getDeclContext(); return genericCtx; }; const auto *affected = getAffectedCtx(req.getFirstType()); if (affected != genericCtx) { auto *NTD = affected->getAsNominalTypeOrNominalTypeExtensionContext(); emitDiagnostic(anchor->getLoc(), diag::type_does_not_conform_in_decl_ref, AffectedDecl->getDescriptiveKind(), AffectedDecl->getFullName(), NTD->getDeclaredType(), type, protocolType); } else { emitDiagnostic(anchor->getLoc(), diag::type_does_not_conform_decl_owner, AffectedDecl->getDescriptiveKind(), AffectedDecl->getFullName(), type, protocolType); } emitDiagnostic(affected->getAsDeclOrDeclExtensionContext(), diag::where_type_does_not_conform_type, req.getFirstType(), type); } return true; } bool LabelingFailure::diagnose() { auto &cs = getConstraintSystem(); auto *call = cast(getAnchor()); return diagnoseArgumentLabelError(cs.getASTContext(), call->getArg(), CorrectLabels, isa(call->getFn())); } bool NoEscapeFuncToTypeConversionFailure::diagnose() { auto *anchor = getAnchor(); if (ConvertTo) { emitDiagnostic(anchor->getLoc(), diag::converting_noescape_to_type, ConvertTo); return true; } auto path = getLocator()->getPath(); if (path.empty()) return false; auto &last = path.back(); if (last.getKind() != ConstraintLocator::Archetype) return false; auto *archetype = last.getArchetype(); emitDiagnostic(anchor->getLoc(), diag::converting_noescape_to_type, archetype); return true; }