Skip to content

Unify ConstraintGraph change tracking with SavedTypeVariableBindings #76759

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Sep 30, 2024
Merged
169 changes: 169 additions & 0 deletions include/swift/Sema/CSTrail.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
//===--- CSTrail.h - Constraint Solver Trail --------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2024 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 \c SolverTrail class, which records the decisions taken
// while attempting to find a solution.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_SEMA_CSTRAIL_H
#define SWIFT_SEMA_CSTRAIL_H

#include <vector>

namespace llvm {
class raw_ostream;
}

namespace swift {

class TypeBase;
class TypeVariableType;

namespace constraints {

class Constraint;

class SolverTrail {
public:

/// The kind of change made to the graph.
enum class ChangeKind {
/// Added a type variable to the constraint graph.
AddedTypeVariable,
/// Added a new constraint to the constraint graph.
AddedConstraint,
/// Removed an existing constraint from the constraint graph.
RemovedConstraint,
/// Extended the equivalence class of a type variable in the constraint graph.
ExtendedEquivalenceClass,
/// Added a fixed binding for a type variable in the constraint graph.
BoundTypeVariable,
/// Introduced a type variable's fixed type to inference.
IntroducedToInference,
/// Set the fixed type or parent and flags for a type variable.
UpdatedTypeVariable,
};

/// A change made to the constraint system.
///
/// Each change can be undone (once, and in reverse order) by calling the
/// undo() method.
class Change {
public:
/// The kind of change.
ChangeKind Kind;

union {
TypeVariableType *TypeVar;
Constraint *TheConstraint;

struct {
/// The type variable whose equivalence class was extended.
TypeVariableType *TypeVar;

/// The previous size of the equivalence class.
unsigned PrevSize;
} EquivClass;

struct {
/// The type variable being bound to a fixed type.
TypeVariableType *TypeVar;

/// The fixed type to which the type variable was bound.
TypeBase *FixedType;
} Binding;

struct {
/// The type variable being updated.
TypeVariableType *TypeVar;

/// The representative of the equivalence class, or the fixed type.
llvm::PointerUnion<TypeVariableType *, TypeBase *> ParentOrFixed;

/// The saved value of TypeVariableType::Implementation::getRawOptions().
unsigned Options;
} Update;
};

Change() : Kind(ChangeKind::AddedTypeVariable), TypeVar(nullptr) { }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still need this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's needed because eg this calls the default constructor and it won't synthesize one for us because of the unions:

SolverTrail::Change
SolverTrail::Change::addedConstraint(Constraint *constraint) {
  Change result;
  result.Kind = ChangeKind::AddedConstraint;
  result.TheConstraint = constraint;
  return result;
}

If there's another way to initialize this without the default constructor I can refactor it later, because I'll be adding lots more Change kinds soon. :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a similar pattern in SyntacticElementTarget, we just need a fee constructors. No need to change anything in this PR if you are going to follow up anyway!


/// Create a change that added a type variable.
static Change addedTypeVariable(TypeVariableType *typeVar);

/// Create a change that added a constraint.
static Change addedConstraint(Constraint *constraint);

/// Create a change that removed a constraint.
static Change removedConstraint(Constraint *constraint);

/// Create a change that extended an equivalence class.
static Change extendedEquivalenceClass(TypeVariableType *typeVar,
unsigned prevSize);

/// Create a change that bound a type variable to a fixed type.
static Change boundTypeVariable(TypeVariableType *typeVar, Type fixed);

/// Create a change that introduced a type variable to inference.
static Change introducedToInference(TypeVariableType *typeVar, Type fixed);

/// Create a change that updated a type variable.
static Change updatedTypeVariable(
TypeVariableType *typeVar,
llvm::PointerUnion<TypeVariableType *, TypeBase *> parentOrFixed,
unsigned options);

/// Undo this change, reverting the constraint graph to the state it
/// had prior to this change.
///
/// Changes must be undone in stack order.
void undo(ConstraintSystem &cs) const;

void dump(llvm::raw_ostream &out, ConstraintSystem &cs,
unsigned indent = 0) const;
};

SolverTrail(ConstraintSystem &cs) : CS(cs) {}

~SolverTrail();

SolverTrail(const SolverTrail &) = delete;
SolverTrail &operator=(const SolverTrail &) = delete;

bool isUndoActive() const { return UndoActive; }

void recordChange(Change change);

void dumpActiveScopeChanges(llvm::raw_ostream &out,
unsigned fromIndex,
unsigned indent = 0) const;

unsigned size() const {
return Changes.size();
}

void undo(unsigned toIndex);

private:
ConstraintSystem &CS;

/// The list of changes made to this constraint system.
std::vector<Change> Changes;

bool UndoActive = false;
unsigned Total = 0;
unsigned Max = 0;
};

} // namespace constraints
} // namespace swift

#endif // SWIFT_SEMA_CSTRAIL_H
107 changes: 22 additions & 85 deletions include/swift/Sema/ConstraintGraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ namespace constraints {

class Constraint;
class ConstraintGraph;
class ConstraintGraphScope;
class ConstraintSystem;
class TypeVariableBinding;

Expand All @@ -58,6 +57,13 @@ class ConstraintGraphNode {
/// Retrieve the type variable this node represents.
TypeVariableType *getTypeVariable() const { return TypeVar; }

void reset();

void initTypeVariable(TypeVariableType *typeVar) {
ASSERT(!TypeVar);
TypeVar = typeVar;
}

/// Retrieve the set of constraints that mention this type variable.
///
/// These are the hyperedges of the graph, connecting this node to
Expand Down Expand Up @@ -148,9 +154,7 @@ class ConstraintGraphNode {
void introduceToInference(Type fixedType);

/// Opposite of \c introduceToInference(Type)
void
retractFromInference(Type fixedType,
SmallPtrSetImpl<TypeVariableType *> &referencedVars);
void retractFromInference(Type fixedType);

/// Drop all previously collected bindings and re-infer based on the
/// current set constraints associated with this equivalence class.
Expand Down Expand Up @@ -216,6 +220,7 @@ class ConstraintGraphNode {
friend class ConstraintGraph;
friend class ConstraintSystem;
friend class TypeVariableBinding;
friend class SolverTrail;
};

/// A graph that describes the relationships among the various type variables
Expand Down Expand Up @@ -266,6 +271,9 @@ class ConstraintGraph {
/// Bind the given type variable to the given fixed type.
void bindTypeVariable(TypeVariableType *typeVar, Type fixedType);

/// Introduce the type variable's fixed type to inference.
void introduceToInference(TypeVariableType *typeVar, Type fixedType);

/// Describes which constraints \c gatherConstraints should gather.
enum class GatheringKind {
/// Gather constraints associated with all of the variables within the
Expand Down Expand Up @@ -387,7 +395,6 @@ class ConstraintGraph {
/// Print the graph.
void print(ArrayRef<TypeVariableType *> typeVars, llvm::raw_ostream &out);
void dump(llvm::raw_ostream &out);
void dumpActiveScopeChanges(llvm::raw_ostream &out, unsigned indent = 0);

// FIXME: Potentially side-effectful.
SWIFT_DEBUG_HELPER(void dump());
Expand Down Expand Up @@ -423,6 +430,12 @@ class ConstraintGraph {
/// caution.
void unbindTypeVariable(TypeVariableType *typeVar, Type fixedType);

/// Retract the given type variable from inference.
///
/// Note that this change is not recorded and cannot be undone. Use with
/// caution.
void retractFromInference(TypeVariableType *typeVar, Type fixedType);

/// Perform edge contraction on the constraint graph, merging equivalence
/// classes until a fixed point is reached.
bool contractEdges();
Expand All @@ -436,90 +449,14 @@ class ConstraintGraph {
/// Constraints that are "orphaned" because they contain no type variables.
SmallVector<Constraint *, 4> OrphanedConstraints;

/// Unused nodes.
SmallVector<ConstraintGraphNode *> FreeList;

/// Increment the number of constraints considered per attempt
/// to contract constraint graph edges.
void incrementConstraintsPerContractionCounter();

/// The kind of change made to the graph.
enum class ChangeKind {
/// Added a type variable.
AddedTypeVariable,
/// Added a new constraint.
AddedConstraint,
/// Removed an existing constraint
RemovedConstraint,
/// Extended the equivalence class of a type variable.
ExtendedEquivalenceClass,
/// Added a fixed binding for a type variable.
BoundTypeVariable,
};

/// A change made to the constraint graph.
///
/// Each change can be undone (once, and in reverse order) by calling the
/// undo() method.
class Change {
public:
/// The kind of change.
ChangeKind Kind;

union {
TypeVariableType *TypeVar;
Constraint *TheConstraint;

struct {
/// The type variable whose equivalence class was extended.
TypeVariableType *TypeVar;

/// The previous size of the equivalence class.
unsigned PrevSize;
} EquivClass;

struct {
/// The type variable being bound to a fixed type.
TypeVariableType *TypeVar;

/// The fixed type to which the type variable was bound.
TypeBase *FixedType;
} Binding;
};

Change() : Kind(ChangeKind::AddedTypeVariable), TypeVar(nullptr) { }

/// Create a change that added a type variable.
static Change addedTypeVariable(TypeVariableType *typeVar);

/// Create a change that added a constraint.
static Change addedConstraint(Constraint *constraint);

/// Create a change that removed a constraint.
static Change removedConstraint(Constraint *constraint);

/// Create a change that extended an equivalence class.
static Change extendedEquivalenceClass(TypeVariableType *typeVar,
unsigned prevSize);

/// Create a change that bound a type variable to a fixed type.
static Change boundTypeVariable(TypeVariableType *typeVar, Type fixed);

/// Undo this change, reverting the constraint graph to the state it
/// had prior to this change.
///
/// Changes must be undone in stack order.
void undo(ConstraintGraph &cg);
};

/// The currently active scope, or null if we aren't tracking changes made
/// to the constraint graph.
ConstraintGraphScope *ActiveScope = nullptr;

/// The set of changes made to this constraint graph.
///
/// As the constraint graph is extended and mutated, additional changes are
/// introduced into this vector. Each scope
llvm::SmallVector<Change, 4> Changes;

friend class ConstraintGraphScope;
friend class SolverTrail;
};

} // end namespace constraints
Expand Down
Loading