Revision ce767b8efdf56a4a1796e7742fc1ba7b7710a30a authored by Dmitri Gribenko on 21 January 2016, 22:29:30 UTC, committed by Dmitri Gribenko on 21 January 2016, 22:30:05 UTC
1 parent 4a74e2e
Raw File
IterativeTypeChecker.cpp
//===--- IterativeTypeChecker.cpp - Iterative Type Checker ----------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
//  This file implements the IterativeTypeChecker class, which
//  performs iterative type checking by tracking the set of
//  outstanding type-checking requests and servicing them as needed.
//
//===----------------------------------------------------------------------===//

#include "TypeChecker.h"
#include "swift/Sema/IterativeTypeChecker.h"
#include "swift/AST/Decl.h"
#include "swift/AST/DiagnosticsSema.h"
#include "swift/Basic/Defer.h"
using namespace swift;

ASTContext &IterativeTypeChecker::getASTContext() const {
  return TC.Context;
}

DiagnosticEngine &IterativeTypeChecker::getDiags() const {
  return getASTContext().Diags;
}

void IterativeTypeChecker::process(
       TypeCheckRequest request,
       UnsatisfiedDependency unsatisfiedDependency) {
  switch (request.getKind()) {
#define TYPE_CHECK_REQUEST(Request,PayloadName)                   \
  case TypeCheckRequest::Request:                                 \
    return process##Request(request.get##PayloadName##Payload(),  \
                            unsatisfiedDependency);

#include "swift/Sema/TypeCheckRequestKinds.def"
  }
}

/// Determine whether the given request has already been satisfied.
bool IterativeTypeChecker::isSatisfied(TypeCheckRequest request) {
  switch (request.getKind()) {
#define TYPE_CHECK_REQUEST(Request,PayloadName)                         \
  case TypeCheckRequest::Request:                                       \
    return is##Request##Satisfied(request.get##PayloadName##Payload());

#include "swift/Sema/TypeCheckRequestKinds.def"
  }
}

bool IterativeTypeChecker::breakCycle(TypeCheckRequest request) {
  switch (request.getKind()) {
#define TYPE_CHECK_REQUEST(Request,PayloadName)                         \
  case TypeCheckRequest::Request:                                       \
    return breakCycleFor##Request(request.get##PayloadName##Payload());

#include "swift/Sema/TypeCheckRequestKinds.def"
  }  
}

void IterativeTypeChecker::satisfy(TypeCheckRequest request) {
  // If the request has already been satisfied, we're done.
  if (isSatisfied(request)) return;

  // Check for circular dependencies in our requests.
  // FIXME: This stack operation is painfully inefficient.
  auto existingRequest = std::find(ActiveRequests.rbegin(),
                                   ActiveRequests.rend(),
                                   request);
  if (existingRequest != ActiveRequests.rend()) {
    auto first = existingRequest.base();
    --first;
    diagnoseCircularReference(llvm::makeArrayRef(&*first,
                                                 &*ActiveRequests.end()));
    return;
  }

  // Add this request to the stack of active requests.
  ActiveRequests.push_back(request);
  defer { ActiveRequests.pop_back(); };

  while (true) {
    // Process this requirement, enumerating dependencies if anything else needs
    // to be handled first.
    SmallVector<TypeCheckRequest, 4> unsatisfied;
    process(request, [&](TypeCheckRequest dependency) -> bool {
      if (isSatisfied(dependency)) return false;

      // Record the unsatisfied dependency.
      unsatisfied.push_back(dependency);
      return true;
    });

    // If there were no unsatisfied dependencies, we're done.
    if (unsatisfied.empty()) {
      assert(isSatisfied(request));
      break;
    }

    // Recurse to satisfy any unsatisfied dependencies.
    // FIXME: Don't recurse in the iterative type checker, silly!
    for (auto dependency : unsatisfied) {
      satisfy(dependency);
    }
  }
}

//===----------------------------------------------------------------------===//
// Diagnostics
//===----------------------------------------------------------------------===//
void IterativeTypeChecker::diagnoseCircularReference(
       ArrayRef<TypeCheckRequest> requests) {
  bool isFirst = true;
  for (const auto &request : requests) {
    diagnose(request.getLoc(),
             isFirst ? diag::circular_reference
                     : diag::circular_reference_through);

    isFirst = false;
  }

  // Now try to break the cycle.
#ifndef NDEBUG
  bool brokeCycle = false;
#endif
  for (const auto &request : reverse(requests)) {
    if (breakCycle(request)) {
#ifndef NDEBUG
      brokeCycle = true;
#endif
      break;
    }
  }

  assert(brokeCycle && "Will the cycle be unbroken?");
}
back to top