Skip to content

Commit 3b761dc

Browse files
committed
[Constraint system] Add a SolutionApplicationTarget-based typeCheckExpression
Rework most of typeCheckExpression() to use SolutionApplicationTarget, folding more information into that data structure and sinking more expression-checking behavior down into the more general solver and solution-application code.
1 parent 3190433 commit 3b761dc

File tree

6 files changed

+150
-98
lines changed

6 files changed

+150
-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: 53 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -2058,55 +2058,41 @@ bool GenericRequirementsCheckListener::diagnoseUnsatisfiedRequirement(
20582058
return false;
20592059
}
20602060

2061-
/// Whether the contextual type provided for the given purpose is only a
2062-
/// hint, and not a requirement.
2063-
static bool contextualTypeIsOnlyAHint(ContextualTypePurpose ctp,
2064-
TypeCheckExprOptions options) {
2065-
switch (ctp) {
2066-
case CTP_Initialization:
2067-
return !options.contains(
2068-
TypeCheckExprFlags::ConvertTypeIsOpaqueReturnType);
2069-
case CTP_ForEachStmt:
2070-
return true;
2071-
case CTP_Unused:
2072-
case CTP_ReturnStmt:
2073-
case CTP_ReturnSingleExpr:
2074-
case CTP_YieldByValue:
2075-
case CTP_YieldByReference:
2076-
case CTP_ThrowStmt:
2077-
case CTP_EnumCaseRawValue:
2078-
case CTP_DefaultParameter:
2079-
case CTP_AutoclosureDefaultParameter:
2080-
case CTP_CalleeResult:
2081-
case CTP_CallArgument:
2082-
case CTP_ClosureResult:
2083-
case CTP_ArrayElement:
2084-
case CTP_DictionaryKey:
2085-
case CTP_DictionaryValue:
2086-
case CTP_CoerceOperand:
2087-
case CTP_AssignSource:
2088-
case CTP_SubscriptAssignSource:
2089-
case CTP_Condition:
2090-
case CTP_CannotFail:
2091-
return false;
2092-
}
2093-
}
2094-
20952061
#pragma mark High-level entry points
20962062
Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc,
20972063
TypeLoc convertType,
20982064
ContextualTypePurpose convertTypePurpose,
20992065
TypeCheckExprOptions options,
21002066
ExprTypeCheckListener *listener,
21012067
ConstraintSystem *baseCS) {
2068+
SolutionApplicationTarget target(
2069+
expr, convertTypePurpose, convertType,
2070+
options.contains(TypeCheckExprFlags::IsDiscarded));
2071+
auto resultType = typeCheckExpression(target, dc, options, listener, baseCS);
2072+
if (!resultType) {
2073+
return Type();
2074+
}
2075+
2076+
expr = resultType->getAsExpr();
2077+
return expr->getType();
2078+
}
2079+
2080+
Optional<SolutionApplicationTarget>
2081+
TypeChecker::typeCheckExpression(
2082+
SolutionApplicationTarget target,
2083+
DeclContext *dc,
2084+
TypeCheckExprOptions options,
2085+
ExprTypeCheckListener *listener,
2086+
ConstraintSystem *baseCS) {
21022087
auto &Context = dc->getASTContext();
2088+
Expr *expr = target.getAsExpr();
21032089
FrontendStatsTracer StatsTracer(Context.Stats, "typecheck-expr", expr);
21042090
PrettyStackTraceExpr stackTrace(Context, "type-checking", expr);
21052091

21062092
// First, pre-check the expression, validating any types that occur in the
21072093
// expression and folding sequence expressions.
21082094
if (ConstraintSystem::preCheckExpression(expr, dc, baseCS)) {
2109-
return Type();
2095+
return None;
21102096
}
21112097

21122098
// Construct a constraint system from this expression.
@@ -2124,45 +2110,23 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc,
21242110
ConstraintSystem cs(dc, csOptions);
21252111
cs.baseCS = baseCS;
21262112

2127-
// Verify that a purpose was specified if a convertType was. Note that it is
2128-
// ok to have a purpose without a convertType (which is used for call
2129-
// return types).
2130-
assert((!convertType.getType() || convertTypePurpose != CTP_Unused) &&
2131-
"Purpose for conversion type was not specified");
2132-
2133-
// Take a look at the conversion type to check to make sure it is sensible.
2134-
if (auto type = convertType.getType()) {
2135-
// If we're asked to convert to an UnresolvedType, then ignore the request.
2136-
// This happens when CSDiags nukes a type.
2137-
if (type->is<UnresolvedType>() ||
2138-
(type->is<MetatypeType>() && type->hasUnresolvedType())) {
2139-
convertType = TypeLoc();
2140-
convertTypePurpose = CTP_Unused;
2141-
}
2142-
}
2143-
2144-
// For an @autoclosure default parameter, we want to convert to the result
2145-
// type. Stash the autoclosure default parameter type.
2146-
FunctionType *autoclosureDefaultParamType = nullptr;
2147-
if (convertTypePurpose == CTP_AutoclosureDefaultParameter) {
2148-
autoclosureDefaultParamType = convertType.getType()->castTo<FunctionType>();
2149-
convertType.setType(autoclosureDefaultParamType->getResult());
2150-
}
2151-
21522113
// Tell the constraint system what the contextual type is. This informs
21532114
// diagnostics and is a hint for various performance optimizations.
21542115
// FIXME: Look through LoadExpr. This is an egregious hack due to the
21552116
// way typeCheckExprIndependently works.
2117+
TypeLoc convertType = target.getExprConversionTypeLoc();
21562118
Expr *contextualTypeExpr = expr;
21572119
if (auto loadExpr = dyn_cast_or_null<LoadExpr>(contextualTypeExpr))
21582120
contextualTypeExpr = loadExpr->getSubExpr();
21592121
cs.setContextualType(
2160-
contextualTypeExpr, convertType, convertTypePurpose,
2122+
contextualTypeExpr, convertType,
2123+
target.getExprContextualTypePurpose(),
21612124
options.contains(TypeCheckExprFlags::ConvertTypeIsOpaqueReturnType));
21622125

21632126
// If the convertType is *only* provided for that hint, then null it out so
21642127
// that we don't later treat it as an actual conversion constraint.
2165-
if (contextualTypeIsOnlyAHint(convertTypePurpose, options))
2128+
if (target.contextualTypeIsOnlyAHint(
2129+
options.contains(TypeCheckExprFlags::ConvertTypeIsOpaqueReturnType)))
21662130
convertType = TypeLoc();
21672131

21682132
// If the client can handle unresolved type variables, leave them in the
@@ -2179,56 +2143,57 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc,
21792143
cs.getConstraintLocator(expr, LocatorPathElt::ContextualType());
21802144
Type var = cs.createTypeVariable(convertTypeLocator, TVO_CanBindToNoEscape);
21812145
convertTo = getOptionalType(expr->getLoc(), var);
2146+
} else if (target.getExprContextualTypePurpose()
2147+
== CTP_AutoclosureDefaultParameter) {
2148+
// FIXME: Hack around the convertTo adjustment below, which we want to
2149+
// eliminate.
2150+
convertTo = Type(target.getAsAutoclosureParamType());
21822151
}
21832152

21842153
// Attempt to solve the constraint system.
2185-
SolutionApplicationTarget target(
2186-
expr, convertTypePurpose, convertTo,
2187-
options.contains(TypeCheckExprFlags::IsDiscarded));
2188-
auto viable = cs.solve(target, listener, allowFreeTypeVariables);
2154+
SolutionApplicationTarget innerTarget(
2155+
expr, target.getExprContextualTypePurpose(), convertTo,
2156+
target.isDiscardedExpr());
2157+
auto viable = cs.solve(innerTarget, listener, allowFreeTypeVariables);
21892158
if (!viable)
2190-
return Type();
2159+
return None;
21912160

21922161
// If the client allows the solution to have unresolved type expressions,
21932162
// check for them now. We cannot apply the solution with unresolved TypeVars,
21942163
// because they will leak out into arbitrary places in the resultant AST.
21952164
if (options.contains(TypeCheckExprFlags::AllowUnresolvedTypeVariables) &&
21962165
(viable->size() != 1 ||
21972166
(convertType.getType() && convertType.getType()->hasUnresolvedType()))) {
2198-
return ErrorType::get(Context);
2167+
// FIXME: This hack should only be needed for CSDiag.
2168+
target.getAsExpr()->setType(ErrorType::get(Context));
2169+
return target;
21992170
}
22002171

2201-
auto result = target.getAsExpr();
2202-
auto &solution = (*viable)[0];
2203-
if (!result)
2204-
return Type();
2205-
22062172
// Apply this solution to the constraint system.
2173+
// FIXME: This shouldn't be necessary.
2174+
auto &solution = (*viable)[0];
22072175
cs.applySolution(solution);
22082176

22092177
// Apply the solution to the expression.
22102178
bool performingDiagnostics =
22112179
options.contains(TypeCheckExprFlags::SubExpressionDiagnostics);
2212-
// FIXME: HACK!
2213-
target.setExprConversionType(convertType.getType());
2180+
// FIXME: HACK! Copy over the inner target's expression info.
2181+
target.setExpr(innerTarget.getAsExpr());
2182+
if (convertTo.isNull())
2183+
target.setExprConversionType(convertTo);
22142184
auto resultTarget = cs.applySolution(solution, target, performingDiagnostics);
22152185
if (!resultTarget) {
22162186
// Failure already diagnosed, above, as part of applying the solution.
2217-
return Type();
2218-
}
2219-
result = resultTarget->getAsExpr();
2220-
2221-
// For an @autoclosure default parameter type, add the autoclosure
2222-
// conversion.
2223-
if (convertTypePurpose == CTP_AutoclosureDefaultParameter) {
2224-
result = cs.buildAutoClosureExpr(result, autoclosureDefaultParamType);
2187+
return None;
22252188
}
2189+
Expr *result = resultTarget->getAsExpr();
22262190

22272191
// Notify listener that we've applied the solution.
2228-
if (listener)
2192+
if (listener) {
22292193
result = listener->appliedSolution(solution, result);
2230-
if (!result)
2231-
return Type();
2194+
if (!result)
2195+
return None;
2196+
}
22322197

22332198
if (Context.TypeCheckerOpts.DebugConstraintSolver) {
22342199
auto &log = Context.TypeCheckerDebug->getStream();
@@ -2245,8 +2210,8 @@ Type TypeChecker::typeCheckExpression(Expr *&expr, DeclContext *dc,
22452210
performSyntacticExprDiagnostics(result, dc, isExprStmt);
22462211
}
22472212

2248-
expr = result;
2249-
return cs.getType(expr);
2213+
target.setExpr(result);
2214+
return target;
22502215
}
22512216

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

lib/Sema/TypeChecker.h

Lines changed: 8 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,13 @@ 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+
TypeCheckExprOptions options = TypeCheckExprOptions(),
840+
ExprTypeCheckListener *listener = nullptr,
841+
constraints::ConstraintSystem *baseCS = nullptr);
842+
835843
/// Type check the given expression and return its type without
836844
/// applying the solution.
837845
///

test/Constraints/closures.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ func testMap() {
186186
}
187187

188188
// <rdar://problem/22414757> "UnresolvedDot" "in wrong phase" assertion from verifier
189-
[].reduce { $0 + $1 } // expected-error {{cannot invoke 'reduce' with an argument list of type '(_)'}}
189+
[].reduce { $0 + $1 } // expected-error {{cannot invoke 'reduce' with an argument list of type '(<<error type>>)'}}
190190

191191

192192

0 commit comments

Comments
 (0)