blob: 6101e00aeb3093e72cf4ecbcc8c7879d94780153 [file] [log] [blame]
//===--- CSDiagnostics.h - 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 provides necessary abstractions for constraint system diagnostics.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_SEMA_CSDIAGNOSTICS_H
#define SWIFT_SEMA_CSDIAGNOSTICS_H
#include "Constraint.h"
#include "ConstraintSystem.h"
#include "OverloadChoice.h"
#include "swift/AST/Decl.h"
#include "swift/AST/Expr.h"
#include "swift/AST/Types.h"
#include "llvm/ADT/ArrayRef.h"
namespace swift {
namespace constraints {
/// Base class for all of the possible diagnostics,
/// provides most basic information such as location of
/// the problem, parent expression and some utility methods.
class FailureDiagnostic {
Expr *E;
const Solution &solution;
ConstraintLocator *Locator;
Expr *Anchor;
/// Indicates whether locator could be simplified
/// down to anchor expression.
bool HasComplexLocator;
public:
FailureDiagnostic(Expr *expr, const Solution &solution,
ConstraintLocator *locator)
: E(expr), solution(solution), Locator(locator) {
std::tie(Anchor, HasComplexLocator) = computeAnchor();
}
virtual ~FailureDiagnostic();
/// Try to diagnose a problem given affected expression,
/// failure location, types and declarations deduced by
/// constraint system, and other auxiliary information.
///
/// \returns true If the problem has been successfully diagnosed
/// and diagnostic message emitted, false otherwise.
virtual bool diagnose() = 0;
ConstraintSystem &getConstraintSystem() const {
return solution.getConstraintSystem();
}
Expr *getParentExpr() const { return E; }
Expr *getAnchor() const { return Anchor; }
ConstraintLocator *getLocator() const { return Locator; }
Type getType(Expr *expr) const;
/// Resolve type variables present in the raw type, if any.
Type resolveType(Type rawType) const {
return solution.simplifyType(rawType);
}
template <typename... ArgTypes>
InFlightDiagnostic emitDiagnostic(ArgTypes &&... Args) const;
protected:
TypeChecker &getTypeChecker() const { return getConstraintSystem().TC; }
DeclContext *getDC() const { return getConstraintSystem().DC; }
Optional<SelectedOverload>
getOverloadChoiceIfAvailable(ConstraintLocator *locator) const {
return solution.getOverloadChoiceIfAvailable(locator);
}
/// Retrieve overload choice resolved for given locator
/// by the constraint solver.
ResolvedOverloadSetListItem *getResolvedOverload(ConstraintLocator *locator) {
auto resolvedOverload = getConstraintSystem().getResolvedOverloadSets();
while (resolvedOverload) {
if (resolvedOverload->Locator == locator)
return resolvedOverload;
resolvedOverload = resolvedOverload->Previous;
}
return nullptr;
}
/// \returns true is locator hasn't been simplified down to expression.
bool hasComplexLocator() const { return HasComplexLocator; }
private:
/// Compute anchor expression associated with current diagnostic.
std::pair<Expr *, bool> computeAnchor() const;
};
/// Base class for all of the diagnostics related to generic requirement
/// failures, provides common information like failed requirement,
/// declaration where such requirement comes from, etc.
class RequirementFailure : public FailureDiagnostic {
protected:
using PathEltKind = ConstraintLocator::PathElementKind;
using DiagOnDecl = Diag<DescriptiveDeclKind, DeclName, Type, Type>;
using DiagInReference = Diag<DescriptiveDeclKind, DeclName, Type, Type, Type>;
const ValueDecl *AffectedDecl;
/// If possible, find application expression associated
/// with current generic requirement failure, that helps
/// to diagnose failures related to arguments.
const ApplyExpr *Apply = nullptr;
public:
RequirementFailure(Expr *expr, const Solution &solution,
ConstraintLocator *locator)
: FailureDiagnostic(expr, solution, locator), AffectedDecl(getDeclRef()) {
auto *anchor = getAnchor();
expr->forEachChildExpr([&](Expr *subExpr) -> Expr * {
auto *AE = dyn_cast<ApplyExpr>(subExpr);
if (!AE || AE->getFn() != anchor)
return subExpr;
Apply = AE;
return nullptr;
});
}
unsigned getRequirementIndex() const {
auto path = getLocator()->getPath();
assert(!path.empty());
auto &requirementLoc = path.back();
assert(requirementLoc.getKind() == PathEltKind::TypeParameterRequirement);
return requirementLoc.getValue();
}
/// The generic base type where failing requirement comes from.
Type getOwnerType() const;
/// Generic requirement associated with the failure.
const Requirement &getRequirement() const;
virtual Type getLHS() const = 0;
virtual Type getRHS() const = 0;
bool diagnose() override;
protected:
/// Retrieve declaration contextual where current
/// requirement has been introduced.
const DeclContext *getRequirementDC() const;
virtual DiagOnDecl getDiagnosticOnDecl() const = 0;
virtual DiagInReference getDiagnosticInRereference() const = 0;
/// Determine whether it would be possible to diagnose
/// current requirement failure.
bool canDiagnoseFailure() const {
// For static/initializer calls there is going to be
// a separate fix, attached to the argument, which is
// much easier to diagnose.
// For operator calls we can't currently produce a good
// diagnostic, so instead let's refer to expression diagnostics.
return !(Apply && (isOperator(Apply) || isa<TypeExpr>(getAnchor())));
}
static bool isOperator(const ApplyExpr *apply) {
return isa<PrefixUnaryExpr>(apply) || isa<PostfixUnaryExpr>(apply) ||
isa<BinaryExpr>(apply);
}
private:
/// Retrieve declaration associated with failing generic requirement.
ValueDecl *getDeclRef() const;
void emitRequirementNote(const Decl *anchor) const;
};
/// Diagnostics for failed conformance checks originating from
/// generic requirements e.g.
/// ```swift
/// struct S {}
/// func foo<T: Hashable>(_ t: T) {}
/// foo(S())
/// ```
class MissingConformanceFailure final : public RequirementFailure {
Type NonConformingType;
ProtocolDecl *Protocol;
public:
MissingConformanceFailure(Expr *expr, const Solution &solution,
ConstraintLocator *locator,
std::pair<Type, ProtocolDecl *> conformance)
: RequirementFailure(expr, solution, locator),
NonConformingType(conformance.first), Protocol(conformance.second) {}
bool diagnose() override;
private:
/// The type which was expected, by one of the generic requirements,
/// to conform to associated protocol.
Type getLHS() const override { return NonConformingType; }
/// The protocol generic requirement expected associated type to conform to.
Type getRHS() const override { return Protocol->getDeclaredType(); }
protected:
DiagOnDecl getDiagnosticOnDecl() const override {
return diag::type_does_not_conform_decl_owner;
}
DiagInReference getDiagnosticInRereference() const override {
return diag::type_does_not_conform_in_decl_ref;
}
};
/// Diagnose errors associated with missing, extraneous
/// or incorrect labels supplied by arguments, e.g.
/// ```swift
/// func foo(q: String, _ a: Int) {}
/// foo("ultimate quesiton", a: 42)
/// ```
/// Call to `foo` is going to be diagnosed as missing `q:`
/// and having extraneous `a:` labels, with appropriate fix-its added.
class LabelingFailure final : public FailureDiagnostic {
ArrayRef<Identifier> CorrectLabels;
public:
LabelingFailure(const Solution &solution, ConstraintLocator *locator,
ArrayRef<Identifier> labels)
: FailureDiagnostic(nullptr, solution, locator), CorrectLabels(labels) {}
bool diagnose() override;
};
/// Diagnose errors related to converting function type which
/// isn't explicitly '@escaping' to some other type.
class NoEscapeFuncToTypeConversionFailure final : public FailureDiagnostic {
Type ConvertTo;
public:
NoEscapeFuncToTypeConversionFailure(Expr *expr, const Solution &solution,
ConstraintLocator *locator,
Type toType = Type())
: FailureDiagnostic(expr, solution, locator), ConvertTo(toType) {}
bool diagnose() override;
};
class MissingForcedDowncastFailure final : public FailureDiagnostic {
public:
MissingForcedDowncastFailure(Expr *expr, const Solution &solution,
ConstraintLocator *locator)
: FailureDiagnostic(expr, solution, locator) {}
bool diagnose() override;
};
/// Diagnose failures related to passing value of some type
/// to `inout` parameter, without explicitly specifying `&`.
class MissingAddressOfFailure final : public FailureDiagnostic {
public:
MissingAddressOfFailure(Expr *expr, const Solution &solution,
ConstraintLocator *locator)
: FailureDiagnostic(expr, solution, locator) {}
bool diagnose() override;
};
/// Diagnose failures related attempt to implicitly convert types which
/// do not support such implicit converstion.
/// "as" or "as!" has to be specified explicitly in cases like that.
class MissingExplicitConversionFailure final : public FailureDiagnostic {
Type ConvertingTo;
public:
MissingExplicitConversionFailure(Expr *expr, const Solution &solution,
ConstraintLocator *locator, Type toType)
: FailureDiagnostic(expr, solution, locator), ConvertingTo(toType) {}
bool diagnose() override;
private:
bool exprNeedsParensBeforeAddingAs(Expr *expr) {
auto *DC = getDC();
auto &TC = getTypeChecker();
auto asPG = TC.lookupPrecedenceGroup(
DC, DC->getASTContext().Id_CastingPrecedence, SourceLoc());
if (!asPG)
return true;
return exprNeedsParensInsideFollowingOperator(TC, DC, expr, asPG);
}
bool exprNeedsParensAfterAddingAs(Expr *expr, Expr *rootExpr) {
auto *DC = getDC();
auto &TC = getTypeChecker();
auto asPG = TC.lookupPrecedenceGroup(
DC, DC->getASTContext().Id_CastingPrecedence, SourceLoc());
if (!asPG)
return true;
return exprNeedsParensOutsideFollowingOperator(TC, DC, expr, rootExpr,
asPG);
}
};
/// Diagnose failures related to attempting member access on optional base
/// type without optional chaining or force-unwrapping it first.
class MemberAccessOnOptionalBaseFailure final : public FailureDiagnostic {
DeclName Member;
bool ResultTypeIsOptional;
public:
MemberAccessOnOptionalBaseFailure(Expr *expr, const Solution &solution,
ConstraintLocator *locator,
DeclName memberName, bool resultOptional)
: FailureDiagnostic(expr, solution, locator), Member(memberName),
ResultTypeIsOptional(resultOptional) {}
bool diagnose() override;
};
/// Diagnose failures related to use of the unwrapped optional types,
/// which require some type of force-unwrap e.g. "!" or "try!".
class MissingOptionalUnwrapFailure final : public FailureDiagnostic {
public:
MissingOptionalUnwrapFailure(Expr *expr, const Solution &solution,
ConstraintLocator *locator)
: FailureDiagnostic(expr, solution, locator) {}
bool diagnose() override;
};
/// Diagnose errors associated with rvalues in positions
/// where an lvalue is required, such as inout arguments.
class RValueTreatedAsLValueFailure final : public FailureDiagnostic {
public:
RValueTreatedAsLValueFailure(const Solution &solution, ConstraintLocator *locator)
: FailureDiagnostic(nullptr, solution, locator) {}
bool diagnose() override;
};
} // end namespace constraints
} // end namespace swift
#endif // SWIFT_SEMA_CSDIAGNOSTICS_H