[Type checker] Don't re-insert node types when merging solutions.

Merging partial solutions can end up assigning the same type to a
particular typed node (expression, parameter, etc.), which can lead to
unbalanced set/clear when exploring the solution space (and later on,
crashes). Don't re-insert such information.

This is the same approach taken for type variable bindings, but it's
all pretty unfortunate: partial solutions should only record
information relative to their part of the constraint system, which
would save time and memory during solving. Howver, that's too big a
change for right now.

Fixes rdar://problem/50853028.
This commit is contained in:
Doug Gregor
2019-05-16 21:37:34 -07:00
committed by John McCall
parent a183f762cd
commit 2594de613b
3 changed files with 30 additions and 20 deletions

View File

@@ -251,7 +251,8 @@ void ConstraintSystem::applySolution(const Solution &solution) {
// Add the node types back. // Add the node types back.
for (auto &nodeType : solution.addedNodeTypes) { for (auto &nodeType : solution.addedNodeTypes) {
setType(nodeType.first, nodeType.second); if (!hasType(nodeType.first))
setType(nodeType.first, nodeType.second);
} }
// Register the conformances checked along the way to arrive to solution. // Register the conformances checked along the way to arrive to solution.

View File

@@ -571,7 +571,9 @@ struct Score {
}; };
/// An AST node that can gain type information while solving. /// An AST node that can gain type information while solving.
using TypedNode = llvm::PointerUnion3<Expr *, TypeLoc *, ParamDecl *>; using TypedNode =
llvm::PointerUnion3<const Expr *, const TypeLoc *,
const ParamDecl *>;
/// Display a score. /// Display a score.
llvm::raw_ostream &operator<<(llvm::raw_ostream &out, const Score &score); llvm::raw_ostream &operator<<(llvm::raw_ostream &out, const Score &score);
@@ -1756,12 +1758,12 @@ public:
assert(type && "Expected non-null type"); assert(type && "Expected non-null type");
// Record the type. // Record the type.
if (auto expr = node.dyn_cast<Expr *>()) { if (auto expr = node.dyn_cast<const Expr *>()) {
ExprTypes[expr] = type.getPointer(); ExprTypes[expr] = type.getPointer();
} else if (auto typeLoc = node.dyn_cast<TypeLoc *>()) { } else if (auto typeLoc = node.dyn_cast<const TypeLoc *>()) {
TypeLocTypes[typeLoc] = type.getPointer(); TypeLocTypes[typeLoc] = type.getPointer();
} else { } else {
auto param = node.get<ParamDecl *>(); auto param = node.get<const ParamDecl *>();
ParamTypes[param] = type.getPointer(); ParamTypes[param] = type.getPointer();
} }
@@ -1775,26 +1777,18 @@ public:
/// map is used throughout the expression type checker in order to /// map is used throughout the expression type checker in order to
/// avoid mutating expressions until we know we have successfully /// avoid mutating expressions until we know we have successfully
/// type-checked them. /// type-checked them.
void setType(Expr *E, Type T) {
setType(TypedNode(E), T);
}
void setType(TypeLoc &L, Type T) { void setType(TypeLoc &L, Type T) {
setType(TypedNode(&L), T); setType(TypedNode(&L), T);
} }
void setType(ParamDecl *P, Type T) {
setType(TypedNode(P), T);
}
/// Erase the type for the given node. /// Erase the type for the given node.
void eraseType(TypedNode node) { void eraseType(TypedNode node) {
if (auto expr = node.dyn_cast<Expr *>()) { if (auto expr = node.dyn_cast<const Expr *>()) {
ExprTypes.erase(expr); ExprTypes.erase(expr);
} else if (auto typeLoc = node.dyn_cast<TypeLoc *>()) { } else if (auto typeLoc = node.dyn_cast<const TypeLoc *>()) {
TypeLocTypes.erase(typeLoc); TypeLocTypes.erase(typeLoc);
} else { } else {
auto param = node.get<ParamDecl *>(); auto param = node.get<const ParamDecl *>();
ParamTypes.erase(param); ParamTypes.erase(param);
} }
} }
@@ -1812,12 +1806,20 @@ public:
} }
bool hasType(const TypeLoc &L) const { bool hasType(const TypeLoc &L) const {
return TypeLocTypes.find(&L) != TypeLocTypes.end(); return hasType(TypedNode(&L));
} }
bool hasType(const ParamDecl *P) const { /// Check to see if we have a type for a node.
assert(P != nullptr && "Expected non-null parameter!"); bool hasType(TypedNode node) const {
return ParamTypes.find(P) != ParamTypes.end(); assert(!node.isNull() && "Expected non-null node");
if (auto expr = node.dyn_cast<const Expr *>()) {
return ExprTypes.find(expr) != ExprTypes.end();
} else if (auto typeLoc = node.dyn_cast<const TypeLoc *>()) {
return TypeLocTypes.find(typeLoc) != TypeLocTypes.end();
} else {
auto param = node.get<const ParamDecl *>();
return ParamTypes.find(param) != ParamTypes.end();
}
} }
bool hasType(const KeyPathExpr *KP, unsigned I) const { bool hasType(const KeyPathExpr *KP, unsigned I) const {

View File

@@ -117,5 +117,12 @@ func testOverloading(name: String) {
_ = overloadedTuplify(true) { b in _ = overloadedTuplify(true) { b in
b ? "Hello, \(name)" : "Goodbye" b ? "Hello, \(name)" : "Goodbye"
42 42
overloadedTuplify(false) {
$0 ? "Hello, \(name)" : "Goodbye"
42
if b {
"Hello, \(name)"
}
}
} }
} }