//===--- SolutionResult.h - Constraint System Solution ----------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2019 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 defines the SolutionResult class.
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_TYPECHECK_SOLUTION_RESULT_H
#define SWIFT_TYPECHECK_SOLUTION_RESULT_H

#include "llvm/ADT/ArrayRef.h"

namespace swift {

using llvm::ArrayRef;
using llvm::makeArrayRef;

namespace constraints {

class Solution;

/// Describes the result of solving a constraint system, after
/// potentially taking various corrective actions.
class SolutionResult {
public:
  enum Kind : unsigned char {
    /// The constraint system was successfully solved, and one can
    /// retrieve the resulting solution.
    Success,
    /// The constraint system had multiple solutions, none of which
    /// was better than the others.
    Ambiguous,
    /// The constraint system had no solution, and a diagnostic has
    /// already been emitted.
    Error,
    /// The constraint system had no solution, but no diagnostic has
    /// been emitted yet.
    UndiagnosedError,
    /// The constraint system was too complex to solve, but no
    /// diagnostic has been emitted yet.
    TooComplex,
  };

private:
  /// The kind of solution result.
  Kind kind;

  /// Whether the client has emitted a diagnostic.
  unsigned emittedDiagnostic : 1;

  /// The number of solutions owned by this result.
  unsigned numSolutions = 0;

  /// A pointer to the set of solutions, of which there are
  /// \c numSolutions entries.
  Solution *solutions = nullptr;

  /// General constructor for the named constructors.
  SolutionResult(Kind kind) : kind(kind) {
    emittedDiagnostic = false;
  }

public:
  SolutionResult(const SolutionResult &other) = delete;

  SolutionResult(SolutionResult &&other)
      : kind(other.kind), numSolutions(other.numSolutions),
        solutions(other.solutions) {
    emittedDiagnostic = false;
    other.kind = Error;
    other.numSolutions = 0;
    other.solutions = nullptr;
  }

  SolutionResult &operator=(const SolutionResult &other) = delete;
  SolutionResult &operator=(SolutionResult &&other) = delete;

  ~SolutionResult();

  /// Produce a "solved" result, embedding the given solution.
  static SolutionResult forSolved(Solution &&solution);

  /// Produce an "ambiguous" result, providing the set of
  /// potential solutions.
  static SolutionResult forAmbiguous(MutableArrayRef<Solution> solutions);

  /// Produce a "too complex" failure, which was not yet been
  /// diagnosed.
  static SolutionResult forTooComplex() {
    return SolutionResult(TooComplex);
  }

  /// Produce a failure that has already been diagnosed.
  static SolutionResult forError() {
    return SolutionResult(Error);
  }

  /// Produce a failure that has not yet been diagnosed.
  static SolutionResult forUndiagnosedError() {
    return SolutionResult(UndiagnosedError);
  }

  Kind getKind() const{ return kind; }

  /// Retrieve the solution, where there is one.
  const Solution &getSolution() const;

  /// Retrieve the solution, where there is one.
  Solution &&takeSolution() &&;

  /// Retrieve the set of solutions when there is an ambiguity.
  ArrayRef<Solution> getAmbiguousSolutions() const;

  /// Take the set of solutions when there is an ambiguity.
  MutableArrayRef<Solution> takeAmbiguousSolutions() &&;

  /// Whether this solution requires the client to produce a diagnostic.
  bool requiresDiagnostic() const {
    switch (kind) {
    case Success:
    case Ambiguous:
    case Error:
      return false;

    case UndiagnosedError:
    case TooComplex:
      return true;
    }
    llvm_unreachable("invalid diagnostic kind");
  }

  /// Note that the failure has been diagnosed.
  void markAsDiagnosed() {
    emittedDiagnostic = true;
  }
};

} }

#endif /* SWIFT_TYPECHECK_SOLUTION_RESULT_H */
