Skip to content

Commit 01d5c00

Browse files
committed
[Sema] Requestify default arg type checking
This commit introduces a request to type-check a default argument expression and splits `getDefaultValue` into 2 accessors: - `getStructuralDefaultExpr` which retrieves the potentially un-type-checked default argument expression. - `getTypeCheckedDefaultExpr` which retrieves a fully type-checked default argument expression. In addition, this commit adds `hasDefaultExpr`, which allows checking for a default expr without kicking off a request.
1 parent 7ae3d1f commit 01d5c00

15 files changed

+188
-52
lines changed

include/swift/AST/Decl.h

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5176,6 +5176,7 @@ enum class ParamSpecifier : uint8_t {
51765176
/// A function parameter declaration.
51775177
class ParamDecl : public VarDecl {
51785178
friend class DefaultArgumentInitContextRequest;
5179+
friend class DefaultArgumentExprRequest;
51795180

51805181
llvm::PointerIntPair<Identifier, 1, bool> ArgumentNameAndDestructured;
51815182
SourceLoc ParameterNameLoc;
@@ -5186,7 +5187,11 @@ class ParamDecl : public VarDecl {
51865187

51875188
struct StoredDefaultArgument {
51885189
PointerUnion<Expr *, VarDecl *> DefaultArg;
5189-
Initializer *InitContext = nullptr;
5190+
5191+
/// Stores the context for the default argument as well as a bit to
5192+
/// indicate whether the default expression has been type-checked.
5193+
llvm::PointerIntPair<Initializer *, 1, bool> InitContextAndIsTypeChecked;
5194+
51905195
StringRef StringRepresentation;
51915196
CaptureInfo Captures;
51925197
};
@@ -5251,8 +5256,27 @@ class ParamDecl : public VarDecl {
52515256
void setDefaultArgumentKind(DefaultArgumentKind K) {
52525257
Bits.ParamDecl.defaultArgumentKind = static_cast<unsigned>(K);
52535258
}
5254-
5255-
Expr *getDefaultValue() const {
5259+
5260+
/// Whether this parameter has a default argument expression available.
5261+
///
5262+
/// Note that this will return false for deserialized declarations, which only
5263+
/// have a textual representation of their default expression.
5264+
bool hasDefaultExpr() const;
5265+
5266+
/// Retrieve the fully type-checked default argument expression for this
5267+
/// parameter, or \c nullptr if there is no default expression.
5268+
///
5269+
/// Note that while this will produce a type-checked expression for
5270+
/// caller-side default arguments such as \c #function, this is done purely to
5271+
/// check whether the code is valid. Such default arguments get re-created
5272+
/// at the call site in order to have the correct context information.
5273+
Expr *getTypeCheckedDefaultExpr() const;
5274+
5275+
/// Retrieve the potentially un-type-checked default argument expression for
5276+
/// this parameter, which can be queried for information such as its source
5277+
/// range and textual representation. Returns \c nullptr if there is no
5278+
/// default expression.
5279+
Expr *getStructuralDefaultExpr() const {
52565280
if (auto stored = DefaultValueAndFlags.getPointer())
52575281
return stored->DefaultArg.dyn_cast<Expr *>();
52585282
return nullptr;
@@ -5264,7 +5288,13 @@ class ParamDecl : public VarDecl {
52645288
return nullptr;
52655289
}
52665290

5267-
void setDefaultValue(Expr *E);
5291+
/// Sets a new default argument expression for this parameter. This should
5292+
/// only be called internally by ParamDecl and AST walkers.
5293+
///
5294+
/// \param E The new default argument.
5295+
/// \param isTypeChecked Whether this argument should be used as the
5296+
/// parameter's fully type-checked default argument.
5297+
void setDefaultExpr(Expr *E, bool isTypeChecked);
52685298

52695299
void setStoredProperty(VarDecl *var);
52705300

include/swift/AST/TypeCheckRequests.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1865,6 +1865,27 @@ class DefaultArgumentInitContextRequest
18651865
void cacheResult(Initializer *init) const;
18661866
};
18671867

1868+
/// Computes the fully type-checked default argument expression for a given
1869+
/// parameter.
1870+
class DefaultArgumentExprRequest
1871+
: public SimpleRequest<DefaultArgumentExprRequest, Expr *(ParamDecl *),
1872+
CacheKind::SeparatelyCached> {
1873+
public:
1874+
using SimpleRequest::SimpleRequest;
1875+
1876+
private:
1877+
friend SimpleRequest;
1878+
1879+
// Evaluation.
1880+
llvm::Expected<Expr *> evaluate(Evaluator &evaluator, ParamDecl *param) const;
1881+
1882+
public:
1883+
// Separate caching.
1884+
bool isCached() const { return true; }
1885+
Optional<Expr *> getCachedResult() const;
1886+
void cacheResult(Expr *expr) const;
1887+
};
1888+
18681889
// Allow AnyValue to compare two Type values, even though Type doesn't
18691890
// support ==.
18701891
template<>

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ SWIFT_REQUEST(TypeChecker, ClassAncestryFlagsRequest,
3232
SWIFT_REQUEST(TypeChecker, CompareDeclSpecializationRequest,
3333
bool (DeclContext *, ValueDecl *, ValueDecl *, bool), Cached,
3434
NoLocationInfo)
35+
SWIFT_REQUEST(TypeChecker, DefaultArgumentExprRequest,
36+
Expr *(ParamDecl *), SeparatelyCached, NoLocationInfo)
3537
SWIFT_REQUEST(TypeChecker, DefaultArgumentInitContextRequest,
3638
Initializer *(ParamDecl *), SeparatelyCached, NoLocationInfo)
3739
SWIFT_REQUEST(TypeChecker, DefaultDefinitionTypeRequest,

lib/AST/ASTDumper.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -996,14 +996,14 @@ namespace {
996996
getDefaultArgumentKindString(P->getDefaultArgumentKind()));
997997
}
998998

999-
if (P->getDefaultValue() &&
1000-
!P->getDefaultArgumentCaptureInfo().isTrivial()) {
999+
if (P->hasDefaultExpr() &&
1000+
!P->getDefaultArgumentCaptureInfo().isTrivial()) {
10011001
OS << " ";
10021002
P->getDefaultArgumentCaptureInfo().print(
10031003
PrintWithColorRAII(OS, CapturesColor).getOS());
10041004
}
10051005

1006-
if (auto init = P->getDefaultValue()) {
1006+
if (auto init = P->getStructuralDefaultExpr()) {
10071007
OS << " expression=\n";
10081008
printRec(init);
10091009
}

lib/AST/ASTPrinter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2562,7 +2562,7 @@ void PrintAST::printOneParameter(const ParamDecl *param,
25622562
auto BodyName = param->getName();
25632563
switch (Options.ArgAndParamPrinting) {
25642564
case PrintOptions::ArgAndParamPrintingMode::EnumElement:
2565-
if (ArgName.empty() && BodyName.empty() && !param->getDefaultValue()) {
2565+
if (ArgName.empty() && BodyName.empty() && !param->hasDefaultExpr()) {
25662566
// Don't print anything, in the style of a tuple element.
25672567
return;
25682568
}

lib/AST/ASTScopeCreation.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1212,7 +1212,7 @@ ParameterListScope::expandAScopeThatCreatesANewInsertionPoint(
12121212
// Unlike generic parameters or pattern initializers, it cannot refer to a
12131213
// previous parameter.
12141214
for (ParamDecl *pd : params->getArray()) {
1215-
if (pd->getDefaultValue())
1215+
if (pd->hasDefaultExpr())
12161216
scopeCreator
12171217
.constructExpandAndInsertUncheckable<DefaultArgumentInitializerScope>(
12181218
this, pd);
@@ -1535,7 +1535,7 @@ void ClosureBodyScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
15351535
void DefaultArgumentInitializerScope::
15361536
expandAScopeThatDoesNotCreateANewInsertionPoint(
15371537
ScopeCreator &scopeCreator) {
1538-
auto *initExpr = decl->getDefaultValue();
1538+
auto *initExpr = decl->getStructuralDefaultExpr();
15391539
ASTScopeAssert(initExpr,
15401540
"Default argument initializer must have an initializer.");
15411541
scopeCreator.addToScopeTree(initExpr, this);

lib/AST/ASTScopeSourceRange.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ SourceRange AbstractStmtScope::getSourceRangeOfThisASTNode(
225225

226226
SourceRange DefaultArgumentInitializerScope::getSourceRangeOfThisASTNode(
227227
const bool omitAssertions) const {
228-
if (auto *dv = decl->getDefaultValue())
228+
if (auto *dv = decl->getStructuralDefaultExpr())
229229
return dv->getSourceRange();
230230
return SourceRange();
231231
}

lib/AST/ASTWalker.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,10 +1164,10 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
11641164
}
11651165
}
11661166

1167-
if (auto *E = P->getDefaultValue()) {
1167+
if (auto *E = P->getStructuralDefaultExpr()) {
11681168
auto res = doIt(E);
11691169
if (!res) return true;
1170-
P->setDefaultValue(res);
1170+
P->setDefaultExpr(res, /*isTypeChecked*/ (bool)res->getType());
11711171
}
11721172
}
11731173

lib/AST/Decl.cpp

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5999,7 +5999,7 @@ SourceRange ParamDecl::getSourceRange() const {
59995999
// It would be nice to extend the front of the range to show where inout is,
60006000
// but we don't have that location info. Extend the back of the range to the
60016001
// location of the default argument, or the typeloc if they are valid.
6002-
if (auto expr = getDefaultValue()) {
6002+
if (auto expr = getStructuralDefaultExpr()) {
60036003
auto endLoc = expr->getEndLoc();
60046004
if (endLoc.isValid())
60056005
return SourceRange(startLoc, endLoc);
@@ -6048,14 +6048,14 @@ AnyFunctionType::Param ParamDecl::toFunctionParam(Type type) const {
60486048

60496049
Initializer *ParamDecl::getDefaultArgumentInitContextCached() const {
60506050
if (auto *defaultInfo = DefaultValueAndFlags.getPointer())
6051-
return defaultInfo->InitContext;
6051+
return defaultInfo->InitContextAndIsTypeChecked.getPointer();
60526052

60536053
return nullptr;
60546054
}
60556055

60566056
Initializer *ParamDecl::getDefaultArgumentInitContext() const {
60576057
// If this param doesn't need a context, don't bother kicking off a request.
6058-
if (!getDefaultValue() && !getStoredProperty())
6058+
if (!hasDefaultExpr() && !getStoredProperty())
60596059
return nullptr;
60606060

60616061
auto &ctx = getASTContext();
@@ -6064,15 +6064,61 @@ Initializer *ParamDecl::getDefaultArgumentInitContext() const {
60646064
ctx.evaluator, DefaultArgumentInitContextRequest{mutableThis}, nullptr);
60656065
}
60666066

6067-
void ParamDecl::setDefaultValue(Expr *E) {
6067+
bool ParamDecl::hasDefaultExpr() const {
6068+
switch (getDefaultArgumentKind()) {
6069+
case DefaultArgumentKind::None:
6070+
case DefaultArgumentKind::Inherited:
6071+
case DefaultArgumentKind::StoredProperty:
6072+
return false;
6073+
case DefaultArgumentKind::Normal:
6074+
case DefaultArgumentKind::File:
6075+
case DefaultArgumentKind::Line:
6076+
case DefaultArgumentKind::Column:
6077+
case DefaultArgumentKind::Function:
6078+
case DefaultArgumentKind::DSOHandle:
6079+
case DefaultArgumentKind::NilLiteral:
6080+
case DefaultArgumentKind::EmptyArray:
6081+
case DefaultArgumentKind::EmptyDictionary:
6082+
// Check if we have a structural default expr. This ensures we return false
6083+
// for deserialized decls.
6084+
return getStructuralDefaultExpr();
6085+
}
6086+
llvm_unreachable("Unhandled case in switch");
6087+
}
6088+
6089+
Expr *ParamDecl::getTypeCheckedDefaultExpr() const {
6090+
// Don't kick off a request if we know there's no default expr. The only
6091+
// exception is for inherited default args which we need to perform a couple
6092+
// of semantic checks for.
6093+
if (!hasDefaultExpr() &&
6094+
getDefaultArgumentKind() != DefaultArgumentKind::Inherited) {
6095+
return nullptr;
6096+
}
6097+
6098+
auto &ctx = getASTContext();
6099+
return evaluateOrDefault(
6100+
ctx.evaluator, DefaultArgumentExprRequest{const_cast<ParamDecl *>(this)},
6101+
new (ctx) ErrorExpr(getSourceRange(), ErrorType::get(ctx)));
6102+
}
6103+
6104+
void ParamDecl::setDefaultExpr(Expr *E, bool isTypeChecked) {
60686105
if (!DefaultValueAndFlags.getPointer()) {
60696106
if (!E) return;
60706107

60716108
DefaultValueAndFlags.setPointer(
60726109
getASTContext().Allocate<StoredDefaultArgument>());
60736110
}
60746111

6075-
DefaultValueAndFlags.getPointer()->DefaultArg = E;
6112+
auto *defaultInfo = DefaultValueAndFlags.getPointer();
6113+
assert(defaultInfo->DefaultArg.isNull() ||
6114+
defaultInfo->DefaultArg.is<Expr *>());
6115+
6116+
if (!isTypeChecked) {
6117+
assert(!defaultInfo->InitContextAndIsTypeChecked.getInt() &&
6118+
"Can't overwrite type-checked default with un-type-checked default");
6119+
}
6120+
defaultInfo->DefaultArg = E;
6121+
defaultInfo->InitContextAndIsTypeChecked.setInt(isTypeChecked);
60766122
}
60776123

60786124
void ParamDecl::setStoredProperty(VarDecl *var) {
@@ -6083,7 +6129,10 @@ void ParamDecl::setStoredProperty(VarDecl *var) {
60836129
getASTContext().Allocate<StoredDefaultArgument>());
60846130
}
60856131

6086-
DefaultValueAndFlags.getPointer()->DefaultArg = var;
6132+
auto *defaultInfo = DefaultValueAndFlags.getPointer();
6133+
assert(defaultInfo->DefaultArg.isNull() ||
6134+
defaultInfo->DefaultArg.is<VarDecl *>());
6135+
defaultInfo->DefaultArg = var;
60876136
}
60886137

60896138
Type ValueDecl::getFunctionBuilderType() const {
@@ -6117,7 +6166,7 @@ void ParamDecl::setDefaultArgumentInitContext(Initializer *initContext) {
61176166

61186167
auto *defaultInfo = DefaultValueAndFlags.getPointer();
61196168
assert(defaultInfo);
6120-
defaultInfo->InitContext = initContext;
6169+
defaultInfo->InitContextAndIsTypeChecked.setPointer(initContext);
61216170
}
61226171

61236172
void ParamDecl::setDefaultArgumentCaptureInfo(CaptureInfo captures) {
@@ -6265,10 +6314,10 @@ ParamDecl::getDefaultValueStringRepresentation(
62656314
if (!existing.empty())
62666315
return existing;
62676316

6268-
assert(getDefaultValue()
6317+
assert(hasDefaultExpr()
62696318
&& "Normal default argument with no default expression?!");
6270-
return extractInlinableText(getASTContext().SourceMgr, getDefaultValue(),
6271-
scratch);
6319+
return extractInlinableText(getASTContext().SourceMgr,
6320+
getStructuralDefaultExpr(), scratch);
62726321
}
62736322
case DefaultArgumentKind::StoredProperty: {
62746323
assert(DefaultValueAndFlags.getPointer() &&
@@ -6368,7 +6417,7 @@ void DefaultArgumentInitializer::changeFunction(
63686417
}
63696418

63706419
auto param = paramList->get(getIndex());
6371-
if (param->getDefaultValue() || param->getStoredProperty())
6420+
if (param->hasDefaultExpr() || param->getStoredProperty())
63726421
param->setDefaultArgumentInitContext(this);
63736422
}
63746423

lib/AST/TypeCheckRequests.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "swift/AST/Decl.h"
1414
#include "swift/AST/DiagnosticsCommon.h"
1515
#include "swift/AST/DiagnosticsSema.h"
16+
#include "swift/AST/Initializer.h"
1617
#include "swift/AST/Module.h"
1718
#include "swift/AST/NameLookup.h"
1819
#include "swift/AST/PropertyWrappers.h"
@@ -1199,3 +1200,24 @@ void DefaultArgumentInitContextRequest::cacheResult(Initializer *init) const {
11991200
auto *param = std::get<0>(getStorage());
12001201
param->setDefaultArgumentInitContext(init);
12011202
}
1203+
1204+
//----------------------------------------------------------------------------//
1205+
// DefaultArgumentExprRequest computation.
1206+
//----------------------------------------------------------------------------//
1207+
1208+
Optional<Expr *> DefaultArgumentExprRequest::getCachedResult() const {
1209+
auto *param = std::get<0>(getStorage());
1210+
auto *defaultInfo = param->DefaultValueAndFlags.getPointer();
1211+
if (!defaultInfo)
1212+
return None;
1213+
1214+
if (!defaultInfo->InitContextAndIsTypeChecked.getInt())
1215+
return None;
1216+
1217+
return defaultInfo->DefaultArg.get<Expr *>();
1218+
}
1219+
1220+
void DefaultArgumentExprRequest::cacheResult(Expr *expr) const {
1221+
auto *param = std::get<0>(getStorage());
1222+
param->setDefaultExpr(expr, /*isTypeChecked*/ true);
1223+
}

0 commit comments

Comments
 (0)