Skip to content

[Constraint system] Simplify solution application target handling #29635

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 3 commits into from
Feb 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7323,6 +7323,7 @@ Optional<SolutionApplicationTarget> ConstraintSystem::applySolution(
Type convertType = target.getExprConversionType();
auto shouldCoerceToContextualType = [&]() {
return convertType &&
!target.isOptionalSomePatternInit() &&
!(getType(resultExpr)->isUninhabited() &&
getContextualTypePurpose(target.getAsExpr())
== CTP_ReturnSingleExpr);
Expand Down
3 changes: 1 addition & 2 deletions lib/Sema/CSGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3831,8 +3831,7 @@ bool ConstraintSystem::generateConstraints(StmtCondition condition,

case StmtConditionElement::CK_Boolean: {
Expr *condExpr = condElement.getBoolean();
setContextualType(condExpr, TypeLoc::withoutLoc(boolTy), CTP_Condition,
/*isOpaqueReturnType=*/false);
setContextualType(condExpr, TypeLoc::withoutLoc(boolTy), CTP_Condition);

condExpr = generateConstraints(condExpr, dc);
if (!condExpr) {
Expand Down
78 changes: 39 additions & 39 deletions lib/Sema/CSSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9095,57 +9095,57 @@ void ConstraintSystem::addConstraint(ConstraintKind kind, Type first,
}

void ConstraintSystem::addContextualConversionConstraint(
Expr *expr, ContextualTypeInfo contextualType) {
Type convertType = contextualType.getType();
if (convertType.isNull())
Expr *expr, Type conversionType, ContextualTypePurpose purpose,
bool isOpaqueReturnType) {
if (conversionType.isNull())
return;

// Determine the type of the constraint.
auto constraintKind = ConstraintKind::Conversion;
switch (contextualType.purpose) {
case CTP_ReturnStmt:
case CTP_ReturnSingleExpr:
case CTP_Initialization:
if (contextualType.isOpaqueReturnType)
constraintKind = ConstraintKind::OpaqueUnderlyingType;
break;
switch (purpose) {
case CTP_ReturnStmt:
case CTP_ReturnSingleExpr:
case CTP_Initialization:
if (isOpaqueReturnType)
constraintKind = ConstraintKind::OpaqueUnderlyingType;
break;

case CTP_CallArgument:
constraintKind = ConstraintKind::ArgumentConversion;
break;
case CTP_CallArgument:
constraintKind = ConstraintKind::ArgumentConversion;
break;

case CTP_YieldByReference:
// In a by-reference yield, we expect the contextual type to be an
// l-value type, so the result must be bound to that.
constraintKind = ConstraintKind::Bind;
break;
case CTP_YieldByReference:
// In a by-reference yield, we expect the contextual type to be an
// l-value type, so the result must be bound to that.
constraintKind = ConstraintKind::Bind;
break;

case CTP_ArrayElement:
case CTP_AssignSource:
case CTP_CalleeResult:
case CTP_CannotFail:
case CTP_Condition:
case CTP_Unused:
case CTP_YieldByValue:
case CTP_ThrowStmt:
case CTP_EnumCaseRawValue:
case CTP_DefaultParameter:
case CTP_AutoclosureDefaultParameter:
case CTP_ClosureResult:
case CTP_DictionaryKey:
case CTP_DictionaryValue:
case CTP_CoerceOperand:
case CTP_SubscriptAssignSource:
case CTP_ForEachStmt:
break;
case CTP_ArrayElement:
case CTP_AssignSource:
case CTP_CalleeResult:
case CTP_CannotFail:
case CTP_Condition:
case CTP_Unused:
case CTP_YieldByValue:
case CTP_ThrowStmt:
case CTP_EnumCaseRawValue:
case CTP_DefaultParameter:
case CTP_AutoclosureDefaultParameter:
case CTP_ClosureResult:
case CTP_DictionaryKey:
case CTP_DictionaryValue:
case CTP_CoerceOperand:
case CTP_SubscriptAssignSource:
case CTP_ForEachStmt:
break;
}

// Add the constraint.
bool isForSingleExprFunction = (contextualType.purpose == CTP_ReturnSingleExpr);
bool isForSingleExprFunction = (purpose == CTP_ReturnSingleExpr);
auto *convertTypeLocator = getConstraintLocator(
expr, LocatorPathElt::ContextualType(isForSingleExprFunction));
addConstraint(constraintKind, getType(expr), convertType,
convertTypeLocator, /*isFavored*/ true);
addConstraint(constraintKind, getType(expr), conversionType,
convertTypeLocator, /*isFavored*/ true);
}

Type ConstraintSystem::addJoinConstraint(
Expand Down
18 changes: 5 additions & 13 deletions lib/Sema/CSSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -240,8 +240,7 @@ void ConstraintSystem::applySolution(const Solution &solution) {
for (const auto &contextualType : solution.contextualTypes) {
if (!getContextualTypeInfo(contextualType.first)) {
setContextualType(contextualType.first, contextualType.second.typeLoc,
contextualType.second.purpose,
contextualType.second.isOpaqueReturnType);
contextualType.second.purpose);
}
}

Expand Down Expand Up @@ -1243,8 +1242,6 @@ ConstraintSystem::solveImpl(SolutionApplicationTarget &target,
Expr *expr = target.getAsExpr();
Timer.emplace(expr, *this);

Expr *origExpr = expr;

// Try to shrink the system by reducing disjunction domains. This
// goes through every sub-expression and generate its own sub-system, to
// try to reduce the domains of those subexpressions.
Expand All @@ -1261,12 +1258,8 @@ ConstraintSystem::solveImpl(SolutionApplicationTarget &target,
// constraint.
if (Type convertType = target.getExprConversionType()) {
// Determine whether we know more about the contextual type.
ContextualTypePurpose ctp = CTP_Unused;
bool isOpaqueReturnType = false;
if (auto contextualInfo = getContextualTypeInfo(origExpr)) {
ctp = contextualInfo->purpose;
isOpaqueReturnType = contextualInfo->isOpaqueReturnType;
}
ContextualTypePurpose ctp = target.getExprContextualTypePurpose();
bool isOpaqueReturnType = target.infersOpaqueReturnType();

// Substitute type variables in for unresolved types.
if (allowFreeTypeVariables == FreeTypeVariableBinding::UnresolvedType) {
Expand All @@ -1281,9 +1274,8 @@ ConstraintSystem::solveImpl(SolutionApplicationTarget &target,
});
}

ContextualTypeInfo info{
TypeLoc::withoutLoc(convertType), ctp, isOpaqueReturnType};
addContextualConversionConstraint(expr, info);
addContextualConversionConstraint(expr, convertType, ctp,
isOpaqueReturnType);
}

// Notify the listener that we've built the constraint system.
Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/ConstraintSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4053,7 +4053,7 @@ bool SolutionApplicationTarget::contextualTypeIsOnlyAHint() const {
assert(kind == Kind::expression);
switch (expression.contextualPurpose) {
case CTP_Initialization:
return !infersOpaqueReturnType();
return !infersOpaqueReturnType() && !isOptionalSomePatternInit();
case CTP_ForEachStmt:
return true;
case CTP_Unused:
Expand Down
23 changes: 15 additions & 8 deletions lib/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -792,7 +792,6 @@ using OpenedTypeMap =
struct ContextualTypeInfo {
TypeLoc typeLoc;
ContextualTypePurpose purpose;
bool isOpaqueReturnType = false;

Type getType() const { return typeLoc.getType(); }
};
Expand Down Expand Up @@ -1224,11 +1223,11 @@ class SolutionApplicationTarget {
return expression.contextualPurpose;
}

Type getExprConversionType() const {
return getExprConversionTypeLoc().getType();
Type getExprContextualType() const {
return getExprContextualTypeLoc().getType();
}

TypeLoc getExprConversionTypeLoc() const {
TypeLoc getExprContextualTypeLoc() const {
assert(kind == Kind::expression);

// For an @autoclosure parameter, the conversion type is
Expand All @@ -1241,6 +1240,14 @@ class SolutionApplicationTarget {
return expression.convertType;
}

/// Retrieve the type to which an expression should be converted, or
/// a NULL type if no conversion constraint should be generated.
Type getExprConversionType() const {
if (contextualTypeIsOnlyAHint())
return Type();
return getExprContextualType();
}

/// Returns the autoclosure parameter type, or \c nullptr if the
/// expression has a different kind of context.
FunctionType *getAsAutoclosureParamType() const {
Expand Down Expand Up @@ -2328,12 +2335,11 @@ class ConstraintSystem {
}

void setContextualType(
const Expr *expr, TypeLoc T, ContextualTypePurpose purpose,
bool isOpaqueReturnType) {
const Expr *expr, TypeLoc T, ContextualTypePurpose purpose) {
assert(expr != nullptr && "Expected non-null expression!");
assert(contextualTypes.count(expr) == 0 &&
"Already set this contextual type");
contextualTypes[expr] = { T, purpose, isOpaqueReturnType };
contextualTypes[expr] = { T, purpose };
}

Optional<ContextualTypeInfo> getContextualTypeInfo(const Expr *expr) const {
Expand Down Expand Up @@ -2565,7 +2571,8 @@ class ConstraintSystem {

/// Add the appropriate constraint for a contextual conversion.
void addContextualConversionConstraint(
Expr *expr, ContextualTypeInfo contextualType);
Expr *expr, Type conversionType, ContextualTypePurpose purpose,
bool isOpaqueReturnType);

/// Add a "join" constraint between a set of types, producing the common
/// supertype.
Expand Down
40 changes: 13 additions & 27 deletions lib/Sema/TypeCheckConstraints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2107,6 +2107,7 @@ TypeChecker::typeCheckExpression(
target.setExpr(expr);
return None;
}
target.setExpr(expr);

// Construct a constraint system from this expression.
ConstraintSystemOptions csOptions = ConstraintSystemFlags::AllowFixes;
Expand All @@ -2127,46 +2128,34 @@ TypeChecker::typeCheckExpression(
// diagnostics and is a hint for various performance optimizations.
// FIXME: Look through LoadExpr. This is an egregious hack due to the
// way typeCheckExprIndependently works.
TypeLoc convertType = target.getExprConversionTypeLoc();
Expr *contextualTypeExpr = expr;
if (auto loadExpr = dyn_cast_or_null<LoadExpr>(contextualTypeExpr))
contextualTypeExpr = loadExpr->getSubExpr();
cs.setContextualType(
contextualTypeExpr, convertType,
target.getExprContextualTypePurpose(),
target.infersOpaqueReturnType());

// If the convertType is *only* provided for that hint, then null it out so
// that we don't later treat it as an actual conversion constraint.
if (target.contextualTypeIsOnlyAHint())
convertType = TypeLoc();
contextualTypeExpr,
target.getExprContextualTypeLoc(),
target.getExprContextualTypePurpose());

// If the client can handle unresolved type variables, leave them in the
// system.
auto allowFreeTypeVariables = FreeTypeVariableBinding::Disallow;
if (options.contains(TypeCheckExprFlags::AllowUnresolvedTypeVariables))
allowFreeTypeVariables = FreeTypeVariableBinding::UnresolvedType;

Type convertTo = convertType.getType();

// If the target requires an optional of some type, form a new appropriate
// type variable and update the target's type with an optional of that
// type variable.
if (target.isOptionalSomePatternInit()) {
assert(!convertTo && "convertType and type check options conflict");
assert(!target.getExprContextualType() &&
"some pattern cannot have contextual type pre-configured");
auto *convertTypeLocator =
cs.getConstraintLocator(expr, LocatorPathElt::ContextualType());
Type var = cs.createTypeVariable(convertTypeLocator, TVO_CanBindToNoEscape);
convertTo = getOptionalType(expr->getLoc(), var);
} else if (target.getExprContextualTypePurpose()
== CTP_AutoclosureDefaultParameter) {
// FIXME: Hack around the convertTo adjustment below, which we want to
// eliminate.
convertTo = Type(target.getAsAutoclosureParamType());
target.setExprConversionType(getOptionalType(expr->getLoc(), var));
}

// Attempt to solve the constraint system.
SolutionApplicationTarget innerTarget(
expr, dc, target.getExprContextualTypePurpose(), convertTo,
target.isDiscardedExpr());
auto viable = cs.solve(innerTarget, listener, allowFreeTypeVariables);
auto viable = cs.solve(target, listener, allowFreeTypeVariables);
if (!viable) {
target.setExpr(expr);
return None;
Expand All @@ -2177,7 +2166,8 @@ TypeChecker::typeCheckExpression(
// because they will leak out into arbitrary places in the resultant AST.
if (options.contains(TypeCheckExprFlags::AllowUnresolvedTypeVariables) &&
(viable->size() != 1 ||
(convertType.getType() && convertType.getType()->hasUnresolvedType()))) {
(target.getExprConversionType() &&
target.getExprConversionType()->hasUnresolvedType()))) {
// FIXME: This hack should only be needed for CSDiag.
unresolvedTypeExprs = true;
return target;
Expand All @@ -2191,10 +2181,6 @@ TypeChecker::typeCheckExpression(
// Apply the solution to the expression.
bool performingDiagnostics =
options.contains(TypeCheckExprFlags::SubExpressionDiagnostics);
// FIXME: HACK! Copy over the inner target's expression info.
target.setExpr(innerTarget.getAsExpr());
if (convertTo.isNull())
target.setExprConversionType(convertTo);
auto resultTarget = cs.applySolution(solution, target, performingDiagnostics);
if (!resultTarget) {
// Failure already diagnosed, above, as part of applying the solution.
Expand Down