Skip to content

Commit 7148108

Browse files
authored
Merge pull request swiftlang#29595 from DougGregor/generalize-solution-application-target
[Constraint system] Add a SolutionApplicationTarget-based typeCheckExpression
2 parents 288a725 + d8c9ef8 commit 7148108

File tree

5 files changed

+166
-98
lines changed

5 files changed

+166
-98
lines changed

lib/Sema/CSApply.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7345,6 +7345,13 @@ Optional<SolutionApplicationTarget> ConstraintSystem::applySolution(
73457345
if (!resultExpr)
73467346
return None;
73477347

7348+
// For an @autoclosure default parameter type, add the autoclosure
7349+
// conversion.
7350+
if (FunctionType *autoclosureParamType =
7351+
target.getAsAutoclosureParamType()) {
7352+
resultExpr = buildAutoClosureExpr(resultExpr, autoclosureParamType);
7353+
}
7354+
73487355
solution.setExprTypes(resultExpr);
73497356
result.setExpr(resultExpr);
73507357
}

lib/Sema/ConstraintSystem.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3968,3 +3968,62 @@ ValueDecl *ConstraintSystem::findResolvedMemberRef(ConstraintLocator *locator) {
39683968

39693969
return choice.getDecl();
39703970
}
3971+
3972+
SolutionApplicationTarget::SolutionApplicationTarget(
3973+
Expr *expr, ContextualTypePurpose contextualPurpose,
3974+
TypeLoc convertType, bool isDiscarded) {
3975+
// Verify that a purpose was specified if a convertType was. Note that it is
3976+
// ok to have a purpose without a convertType (which is used for call
3977+
// return types).
3978+
assert((!convertType.getType() || contextualPurpose != CTP_Unused) &&
3979+
"Purpose for conversion type was not specified");
3980+
3981+
// Take a look at the conversion type to check to make sure it is sensible.
3982+
if (auto type = convertType.getType()) {
3983+
// If we're asked to convert to an UnresolvedType, then ignore the request.
3984+
// This happens when CSDiags nukes a type.
3985+
if (type->is<UnresolvedType>() ||
3986+
(type->is<MetatypeType>() && type->hasUnresolvedType())) {
3987+
convertType = TypeLoc();
3988+
contextualPurpose = CTP_Unused;
3989+
}
3990+
}
3991+
3992+
kind = Kind::expression;
3993+
expression.expression = expr;
3994+
expression.contextualPurpose = contextualPurpose;
3995+
expression.convertType = convertType;
3996+
expression.isDiscarded = isDiscarded;
3997+
}
3998+
3999+
bool SolutionApplicationTarget::contextualTypeIsOnlyAHint(
4000+
bool isOpaqueReturnType) const {
4001+
assert(kind == Kind::expression);
4002+
switch (expression.contextualPurpose) {
4003+
case CTP_Initialization:
4004+
return !isOpaqueReturnType;
4005+
case CTP_ForEachStmt:
4006+
return true;
4007+
case CTP_Unused:
4008+
case CTP_ReturnStmt:
4009+
case CTP_ReturnSingleExpr:
4010+
case CTP_YieldByValue:
4011+
case CTP_YieldByReference:
4012+
case CTP_ThrowStmt:
4013+
case CTP_EnumCaseRawValue:
4014+
case CTP_DefaultParameter:
4015+
case CTP_AutoclosureDefaultParameter:
4016+
case CTP_CalleeResult:
4017+
case CTP_CallArgument:
4018+
case CTP_ClosureResult:
4019+
case CTP_ArrayElement:
4020+
case CTP_DictionaryKey:
4021+
case CTP_DictionaryValue:
4022+
case CTP_CoerceOperand:
4023+
case CTP_AssignSource:
4024+
case CTP_SubscriptAssignSource:
4025+
case CTP_Condition:
4026+
case CTP_CannotFail:
4027+
return false;
4028+
}
4029+
}

lib/Sema/ConstraintSystem.h

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,13 +1174,7 @@ class SolutionApplicationTarget {
11741174
isDiscarded) { }
11751175

11761176
SolutionApplicationTarget(Expr *expr, ContextualTypePurpose contextualPurpose,
1177-
TypeLoc convertType, bool isDiscarded) {
1178-
kind = Kind::expression;
1179-
expression.expression = expr;
1180-
expression.contextualPurpose = contextualPurpose;
1181-
expression.convertType = convertType;
1182-
expression.isDiscarded = isDiscarded;
1183-
}
1177+
TypeLoc convertType, bool isDiscarded);
11841178

11851179
SolutionApplicationTarget(AnyFunctionRef fn)
11861180
: SolutionApplicationTarget(fn, fn.getBody()) { }
@@ -1207,15 +1201,31 @@ class SolutionApplicationTarget {
12071201
}
12081202

12091203
Type getExprConversionType() const {
1210-
assert(kind == Kind::expression);
1211-
return expression.convertType.getType();
1204+
return getExprConversionTypeLoc().getType();
12121205
}
12131206

12141207
TypeLoc getExprConversionTypeLoc() const {
12151208
assert(kind == Kind::expression);
1209+
1210+
// For an @autoclosure parameter, the conversion type is
1211+
// the result of the function type.
1212+
if (FunctionType *autoclosureParamType = getAsAutoclosureParamType()) {
1213+
return TypeLoc(expression.convertType.getTypeRepr(),
1214+
autoclosureParamType->getResult());
1215+
}
1216+
12161217
return expression.convertType;
12171218
}
12181219

1220+
/// Returns the autoclosure parameter type, or \c nullptr if the
1221+
/// expression has a different kind of context.
1222+
FunctionType *getAsAutoclosureParamType() const {
1223+
assert(kind == Kind::expression);
1224+
if (expression.contextualPurpose == CTP_AutoclosureDefaultParameter)
1225+
return expression.convertType.getType()->castTo<FunctionType>();
1226+
return nullptr;
1227+
}
1228+
12191229
void setExprConversionType(Type type) {
12201230
assert(kind == Kind::expression);
12211231
expression.convertType = TypeLoc::withoutLoc(type);
@@ -1226,6 +1236,9 @@ class SolutionApplicationTarget {
12261236
expression.convertType = type;
12271237
}
12281238

1239+
/// Whether the contextual type is only a hint, rather than a type
1240+
bool contextualTypeIsOnlyAHint(bool isOpaqueReturnType) const;
1241+
12291242
bool isDiscardedExpr() const {
12301243
assert(kind == Kind::expression);
12311244
return expression.isDiscarded;

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 69 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -2001,55 +2001,54 @@ bool GenericRequirementsCheckListener::diagnoseUnsatisfiedRequirement(
20012001
return false;
20022002
}
20032003

2004-
/// Whether the contextual type provided for the given purpose is only a
2005-
/// hint, and not a requirement.
2006-
static bool contextualTypeIsOnlyAHint(ContextualTypePurpose ctp,
2007-
TypeCheckExprOptions options) {
2008-
switch (ctp) {
2009-
case CTP_Initialization:
2010-
return !options.contains(
2011-
TypeCheckExprFlags::ConvertTypeIsOpaqueReturnType);
2012-
case CTP_ForEachStmt:
2013-
return true;
2014-
case CTP_Unused:
2015-
case CTP_ReturnStmt:
2016-
case CTP_ReturnSingleExpr:
2017-
case CTP_YieldByValue:
2018-
case CTP_YieldByReference:
2019-
case CTP_ThrowStmt:
2020-
case CTP_EnumCaseRawValue:
2021-
case CTP_DefaultParameter:
2022-
case CTP_AutoclosureDefaultParameter:
2023-
case CTP_CalleeResult:
2024-
case CTP_CallArgument:
2025-
case CTP_ClosureResult:
2026-
case CTP_ArrayElement:
2027-
case CTP_DictionaryKey:
2028-
case CTP_DictionaryValue:
2029-
case CTP_CoerceOperand:
2030-
case CTP_AssignSource:
2031-
case CTP_SubscriptAssignSource:
2032-
case CTP_Condition:
2033-
case CTP_CannotFail:
2034-
return false;
2035-
}
2036-
}
2037-
20382004
#pragma mark High-level entry points
20392005
Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc,
20402006
TypeLoc convertType,
20412007
ContextualTypePurpose convertTypePurpose,
20422008
TypeCheckExprOptions options,
20432009
ExprTypeCheckListener *listener,
20442010
ConstraintSystem *baseCS) {
2011+
SolutionApplicationTarget target(
2012+
expr, convertTypePurpose, convertType,
2013+
options.contains(TypeCheckExprFlags::IsDiscarded));
2014+
bool unresolvedTypeExprs = false;
2015+
auto resultTarget = typeCheckExpression(
2016+
target, dc, unresolvedTypeExprs, options, listener, baseCS);
2017+
if (!resultTarget) {
2018+
expr = target.getAsExpr();
2019+
return Type();
2020+
}
2021+
2022+
expr = resultTarget->getAsExpr();
2023+
2024+
// HACK for clients that want unresolved types.
2025+
if (unresolvedTypeExprs) {
2026+
return ErrorType::get(dc->getASTContext());
2027+
}
2028+
2029+
2030+
return expr->getType();
2031+
}
2032+
2033+
Optional<SolutionApplicationTarget>
2034+
TypeChecker::typeCheckExpression(
2035+
SolutionApplicationTarget &target,
2036+
DeclContext *dc,
2037+
bool &unresolvedTypeExprs,
2038+
TypeCheckExprOptions options,
2039+
ExprTypeCheckListener *listener,
2040+
ConstraintSystem *baseCS) {
2041+
unresolvedTypeExprs = false;
20452042
auto &Context = dc->getASTContext();
2043+
Expr *expr = target.getAsExpr();
20462044
FrontendStatsTracer StatsTracer(Context.Stats, "typecheck-expr", expr);
20472045
PrettyStackTraceExpr stackTrace(Context, "type-checking", expr);
20482046

20492047
// First, pre-check the expression, validating any types that occur in the
20502048
// expression and folding sequence expressions.
20512049
if (ConstraintSystem::preCheckExpression(expr, dc, baseCS)) {
2052-
return Type();
2050+
target.setExpr(expr);
2051+
return None;
20532052
}
20542053

20552054
// Construct a constraint system from this expression.
@@ -2067,45 +2066,23 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc,
20672066
ConstraintSystem cs(dc, csOptions);
20682067
cs.baseCS = baseCS;
20692068

2070-
// Verify that a purpose was specified if a convertType was. Note that it is
2071-
// ok to have a purpose without a convertType (which is used for call
2072-
// return types).
2073-
assert((!convertType.getType() || convertTypePurpose != CTP_Unused) &&
2074-
"Purpose for conversion type was not specified");
2075-
2076-
// Take a look at the conversion type to check to make sure it is sensible.
2077-
if (auto type = convertType.getType()) {
2078-
// If we're asked to convert to an UnresolvedType, then ignore the request.
2079-
// This happens when CSDiags nukes a type.
2080-
if (type->is<UnresolvedType>() ||
2081-
(type->is<MetatypeType>() && type->hasUnresolvedType())) {
2082-
convertType = TypeLoc();
2083-
convertTypePurpose = CTP_Unused;
2084-
}
2085-
}
2086-
2087-
// For an @autoclosure default parameter, we want to convert to the result
2088-
// type. Stash the autoclosure default parameter type.
2089-
FunctionType *autoclosureDefaultParamType = nullptr;
2090-
if (convertTypePurpose == CTP_AutoclosureDefaultParameter) {
2091-
autoclosureDefaultParamType = convertType.getType()->castTo<FunctionType>();
2092-
convertType.setType(autoclosureDefaultParamType->getResult());
2093-
}
2094-
20952069
// Tell the constraint system what the contextual type is. This informs
20962070
// diagnostics and is a hint for various performance optimizations.
20972071
// FIXME: Look through LoadExpr. This is an egregious hack due to the
20982072
// way typeCheckExprIndependently works.
2073+
TypeLoc convertType = target.getExprConversionTypeLoc();
20992074
Expr *contextualTypeExpr = expr;
21002075
if (auto loadExpr = dyn_cast_or_null<LoadExpr>(contextualTypeExpr))
21012076
contextualTypeExpr = loadExpr->getSubExpr();
21022077
cs.setContextualType(
2103-
contextualTypeExpr, convertType, convertTypePurpose,
2078+
contextualTypeExpr, convertType,
2079+
target.getExprContextualTypePurpose(),
21042080
options.contains(TypeCheckExprFlags::ConvertTypeIsOpaqueReturnType));
21052081

21062082
// If the convertType is *only* provided for that hint, then null it out so
21072083
// that we don't later treat it as an actual conversion constraint.
2108-
if (contextualTypeIsOnlyAHint(convertTypePurpose, options))
2084+
if (target.contextualTypeIsOnlyAHint(
2085+
options.contains(TypeCheckExprFlags::ConvertTypeIsOpaqueReturnType)))
21092086
convertType = TypeLoc();
21102087

21112088
// If the client can handle unresolved type variables, leave them in the
@@ -2122,56 +2099,59 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc,
21222099
cs.getConstraintLocator(expr, LocatorPathElt::ContextualType());
21232100
Type var = cs.createTypeVariable(convertTypeLocator, TVO_CanBindToNoEscape);
21242101
convertTo = getOptionalType(expr->getLoc(), var);
2102+
} else if (target.getExprContextualTypePurpose()
2103+
== CTP_AutoclosureDefaultParameter) {
2104+
// FIXME: Hack around the convertTo adjustment below, which we want to
2105+
// eliminate.
2106+
convertTo = Type(target.getAsAutoclosureParamType());
21252107
}
21262108

21272109
// Attempt to solve the constraint system.
2128-
SolutionApplicationTarget target(
2129-
expr, convertTypePurpose, convertTo,
2130-
options.contains(TypeCheckExprFlags::IsDiscarded));
2131-
auto viable = cs.solve(target, listener, allowFreeTypeVariables);
2132-
if (!viable)
2133-
return Type();
2110+
SolutionApplicationTarget innerTarget(
2111+
expr, target.getExprContextualTypePurpose(), convertTo,
2112+
target.isDiscardedExpr());
2113+
auto viable = cs.solve(innerTarget, listener, allowFreeTypeVariables);
2114+
if (!viable) {
2115+
target.setExpr(expr);
2116+
return None;
2117+
}
21342118

21352119
// If the client allows the solution to have unresolved type expressions,
21362120
// check for them now. We cannot apply the solution with unresolved TypeVars,
21372121
// because they will leak out into arbitrary places in the resultant AST.
21382122
if (options.contains(TypeCheckExprFlags::AllowUnresolvedTypeVariables) &&
21392123
(viable->size() != 1 ||
21402124
(convertType.getType() && convertType.getType()->hasUnresolvedType()))) {
2141-
return ErrorType::get(Context);
2125+
// FIXME: This hack should only be needed for CSDiag.
2126+
unresolvedTypeExprs = true;
2127+
return target;
21422128
}
21432129

2144-
auto result = target.getAsExpr();
2145-
auto &solution = (*viable)[0];
2146-
if (!result)
2147-
return Type();
2148-
21492130
// Apply this solution to the constraint system.
2131+
// FIXME: This shouldn't be necessary.
2132+
auto &solution = (*viable)[0];
21502133
cs.applySolution(solution);
21512134

21522135
// Apply the solution to the expression.
21532136
bool performingDiagnostics =
21542137
options.contains(TypeCheckExprFlags::SubExpressionDiagnostics);
2155-
// FIXME: HACK!
2156-
target.setExprConversionType(convertType.getType());
2138+
// FIXME: HACK! Copy over the inner target's expression info.
2139+
target.setExpr(innerTarget.getAsExpr());
2140+
if (convertTo.isNull())
2141+
target.setExprConversionType(convertTo);
21572142
auto resultTarget = cs.applySolution(solution, target, performingDiagnostics);
21582143
if (!resultTarget) {
21592144
// Failure already diagnosed, above, as part of applying the solution.
2160-
return Type();
2161-
}
2162-
result = resultTarget->getAsExpr();
2163-
2164-
// For an @autoclosure default parameter type, add the autoclosure
2165-
// conversion.
2166-
if (convertTypePurpose == CTP_AutoclosureDefaultParameter) {
2167-
result = cs.buildAutoClosureExpr(result, autoclosureDefaultParamType);
2145+
return None;
21682146
}
2147+
Expr *result = resultTarget->getAsExpr();
21692148

21702149
// Notify listener that we've applied the solution.
2171-
if (listener)
2150+
if (listener) {
21722151
result = listener->appliedSolution(solution, result);
2173-
if (!result)
2174-
return Type();
2152+
if (!result)
2153+
return None;
2154+
}
21752155

21762156
if (Context.TypeCheckerOpts.DebugConstraintSolver) {
21772157
auto &log = Context.TypeCheckerDebug->getStream();
@@ -2188,8 +2168,8 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc,
21882168
performSyntacticExprDiagnostics(result, dc, isExprStmt);
21892169
}
21902170

2191-
expr = result;
2192-
return cs.getType(expr);
2171+
target.setExpr(result);
2172+
return target;
21932173
}
21942174

21952175
Type TypeChecker::typeCheckParameterDefault(Expr *&defaultValue,

lib/Sema/TypeChecker.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ namespace constraints {
4949
enum class ConstraintKind : char;
5050
class ConstraintSystem;
5151
class Solution;
52+
class SolutionApplicationTarget;
5253
class SolutionResult;
5354
}
5455

@@ -832,6 +833,14 @@ class TypeChecker final {
832833
ExprTypeCheckListener *listener = nullptr,
833834
constraints::ConstraintSystem *baseCS = nullptr);
834835

836+
static Optional<constraints::SolutionApplicationTarget>
837+
typeCheckExpression(constraints::SolutionApplicationTarget &target,
838+
DeclContext *dc,
839+
bool &unresolvedTypeExprs,
840+
TypeCheckExprOptions options = TypeCheckExprOptions(),
841+
ExprTypeCheckListener *listener = nullptr,
842+
constraints::ConstraintSystem *baseCS = nullptr);
843+
835844
/// Type check the given expression and return its type without
836845
/// applying the solution.
837846
///

0 commit comments

Comments
 (0)