void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
                                       ExplodedNode *Pred,
                                       ExplodedNodeSet &destNodes) {
  const LocationContext *LCtx = Pred->getLocationContext();
  ProgramStateRef State = Pred->getState();

  SVal Target = UnknownVal();

  if (Optional<SVal> ElidedTarget =
          getObjectUnderConstruction(State, CE, LCtx)) {
    // We've previously modeled an elidable constructor by pretending that it in
    // fact constructs into the correct target. This constructor can therefore
    // be skipped.
    Target = *ElidedTarget;
    StmtNodeBuilder Bldr(Pred, destNodes, *currBldrCtx);
    State = finishObjectConstruction(State, CE, LCtx);
    if (auto L = Target.getAs<Loc>())
      State = State->BindExpr(CE, LCtx, State->getSVal(*L, CE->getType()));
    Bldr.generateNode(CE, Pred, State);
    return;
  }

  // FIXME: Handle arrays, which run the same constructor for every element.
  // For now, we just run the first constructor (which should still invalidate
  // the entire array).

  EvalCallOptions CallOpts;
  auto C = getCurrentCFGElement().getAs<CFGConstructor>();
  assert(C || getCurrentCFGElement().getAs<CFGStmt>());
  const ConstructionContext *CC = C ? C->getConstructionContext() : nullptr;

  switch (CE->getConstructionKind()) {
  case CXXConstructExpr::CK_Complete: {
    std::tie(State, Target) =
        prepareForObjectConstruction(CE, State, LCtx, CC, CallOpts);
    break;
  }
  case CXXConstructExpr::CK_VirtualBase:
    // Make sure we are not calling virtual base class initializers twice.
    // Only the most-derived object should initialize virtual base classes.
    if (const Stmt *Outer = LCtx->getStackFrame()->getCallSite()) {
      const CXXConstructExpr *OuterCtor = dyn_cast<CXXConstructExpr>(Outer);
      if (OuterCtor) {
        switch (OuterCtor->getConstructionKind()) {
        case CXXConstructExpr::CK_NonVirtualBase:
        case CXXConstructExpr::CK_VirtualBase:
          // Bail out!
          destNodes.Add(Pred);
          return;
        case CXXConstructExpr::CK_Complete:
        case CXXConstructExpr::CK_Delegating:
          break;
        }
      }
    }
    LLVM_FALLTHROUGH;
  case CXXConstructExpr::CK_NonVirtualBase:
    // In C++17, classes with non-virtual bases may be aggregates, so they would
    // be initialized as aggregates without a constructor call, so we may have
    // a base class constructed directly into an initializer list without
    // having the derived-class constructor call on the previous stack frame.
    // Initializer lists may be nested into more initializer lists that
    // correspond to surrounding aggregate initializations.
    // FIXME: For now this code essentially bails out. We need to find the
    // correct target region and set it.
    // FIXME: Instead of relying on the ParentMap, we should have the
    // trigger-statement (InitListExpr in this case) passed down from CFG or
    // otherwise always available during construction.
    if (dyn_cast_or_null<InitListExpr>(LCtx->getParentMap().getParent(CE))) {
      MemRegionManager &MRMgr = getSValBuilder().getRegionManager();
      Target = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(CE, LCtx));
      CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true;
      break;
    }
    LLVM_FALLTHROUGH;
  case CXXConstructExpr::CK_Delegating: {
    const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl());
    Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor,
                                              LCtx->getStackFrame());
    SVal ThisVal = State->getSVal(ThisPtr);

    if (CE->getConstructionKind() == CXXConstructExpr::CK_Delegating) {
      Target = ThisVal;
    } else {
      // Cast to the base type.
      bool IsVirtual =
        (CE->getConstructionKind() == CXXConstructExpr::CK_VirtualBase);
      SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, CE->getType(),
                                                         IsVirtual);
      Target = BaseVal;
    }
    break;
  }
  }

  if (State != Pred->getState()) {
    static SimpleProgramPointTag T("ExprEngine",
                                   "Prepare for object construction");
    ExplodedNodeSet DstPrepare;
    StmtNodeBuilder BldrPrepare(Pred, DstPrepare, *currBldrCtx);
    BldrPrepare.generateNode(CE, Pred, State, &T, ProgramPoint::PreStmtKind);
    assert(DstPrepare.size() <= 1);
    if (DstPrepare.size() == 0)
      return;
    Pred = *BldrPrepare.begin();
  }

  CallEventManager &CEMgr = getStateManager().getCallEventManager();
  CallEventRef<CXXConstructorCall> Call =
    CEMgr.getCXXConstructorCall(CE, Target.getAsRegion(), State, LCtx);

  ExplodedNodeSet DstPreVisit;
  getCheckerManager().runCheckersForPreStmt(DstPreVisit, Pred, CE, *this);

  // FIXME: Is it possible and/or useful to do this before PreStmt?
  ExplodedNodeSet PreInitialized;
  {
    StmtNodeBuilder Bldr(DstPreVisit, PreInitialized, *currBldrCtx);
    for (ExplodedNodeSet::iterator I = DstPreVisit.begin(),
                                   E = DstPreVisit.end();
         I != E; ++I) {
      ProgramStateRef State = (*I)->getState();
      if (CE->requiresZeroInitialization()) {
        // FIXME: Once we properly handle constructors in new-expressions, we'll
        // need to invalidate the region before setting a default value, to make
        // sure there aren't any lingering bindings around. This probably needs
        // to happen regardless of whether or not the object is zero-initialized
        // to handle random fields of a placement-initialized object picking up
        // old bindings. We might only want to do it when we need to, though.
        // FIXME: This isn't actually correct for arrays -- we need to zero-
        // initialize the entire array, not just the first element -- but our
        // handling of arrays everywhere else is weak as well, so this shouldn't
        // actually make things worse. Placement new makes this tricky as well,
        // since it's then possible to be initializing one part of a multi-
        // dimensional array.
        State = State->bindDefaultZero(Target, LCtx);
      }

      Bldr.generateNode(CE, *I, State, /*tag=*/nullptr,
                        ProgramPoint::PreStmtKind);
    }
  }

  ExplodedNodeSet DstPreCall;
  getCheckerManager().runCheckersForPreCall(DstPreCall, PreInitialized,
                                            *Call, *this);

  ExplodedNodeSet DstEvaluated;
  StmtNodeBuilder Bldr(DstPreCall, DstEvaluated, *currBldrCtx);

  if (CE->getConstructor()->isTrivial() &&
      CE->getConstructor()->isCopyOrMoveConstructor() &&
      !CallOpts.IsArrayCtorOrDtor) {
    // FIXME: Handle other kinds of trivial constructors as well.
    for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
         I != E; ++I)
      performTrivialCopy(Bldr, *I, *Call);

  } else {
    for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
         I != E; ++I)
      defaultEvalCall(Bldr, *I, *Call, CallOpts);
  }

  // If the CFG was constructed without elements for temporary destructors
  // and the just-called constructor created a temporary object then
  // stop exploration if the temporary object has a noreturn constructor.
  // This can lose coverage because the destructor, if it were present
  // in the CFG, would be called at the end of the full expression or
  // later (for life-time extended temporaries) -- but avoids infeasible
  // paths when no-return temporary destructors are used for assertions.
  const AnalysisDeclContext *ADC = LCtx->getAnalysisDeclContext();
  if (!ADC->getCFGBuildOptions().AddTemporaryDtors) {
    const MemRegion *Target = Call->getCXXThisVal().getAsRegion();
    if (Target && isa<CXXTempObjectRegion>(Target) &&
        Call->getDecl()->getParent()->isAnyDestructorNoReturn()) {

      // If we've inlined the constructor, then DstEvaluated would be empty.
      // In this case we still want a sink, which could be implemented
      // in processCallExit. But we don't have that implemented at the moment,
      // so if you hit this assertion, see if you can avoid inlining
      // the respective constructor when analyzer-config cfg-temporary-dtors
      // is set to false.
      // Otherwise there's nothing wrong with inlining such constructor.
      assert(!DstEvaluated.empty() &&
             "We should not have inlined this constructor!");

      for (ExplodedNode *N : DstEvaluated) {
        Bldr.generateSink(CE, N, N->getState());
      }

      // There is no need to run the PostCall and PostStmt checker
      // callbacks because we just generated sinks on all nodes in th
      // frontier.
      return;
    }
  }

  ExplodedNodeSet DstPostArgumentCleanup;
  for (auto I : DstEvaluated)
    finishArgumentConstruction(DstPostArgumentCleanup, I, *Call);

  // If there were other constructors called for object-type arguments
  // of this constructor, clean them up.
  ExplodedNodeSet DstPostCall;
  getCheckerManager().runCheckersForPostCall(DstPostCall,
                                             DstPostArgumentCleanup,
                                             *Call, *this);
  getCheckerManager().runCheckersForPostStmt(destNodes, DstPostCall, CE, *this);
}