Skip to content

[SR-2757][Sema] Mark VarDecl in capture lists #6490

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 1 commit into from
Jan 6, 2017
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
36 changes: 22 additions & 14 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,13 +232,13 @@ class alignas(1 << DeclAlignInBits) Decl {
/// \brief Whether we've already performed early attribute validation.
/// FIXME: This is ugly.
unsigned EarlyAttrValidation : 1;

/// \brief Whether this declaration is currently being validated.
unsigned BeingValidated : 1;
};
enum { NumDeclBits = 11 };
static_assert(NumDeclBits <= 32, "fits in an unsigned");

class PatternBindingDeclBitfields {
friend class PatternBindingDecl;
unsigned : NumDeclBits;
Expand Down Expand Up @@ -284,7 +284,7 @@ class alignas(1 << DeclAlignInBits) Decl {
class VarDeclBitfields {
friend class VarDecl;
unsigned : NumAbstractStorageDeclBits;

/// \brief Whether this property is a type property (currently unfortunately
/// called 'static').
unsigned IsStatic : 1;
Expand All @@ -293,11 +293,14 @@ class alignas(1 << DeclAlignInBits) Decl {
/// once (either in its declaration, or once later), making it immutable.
unsigned IsLet : 1;

/// \brief Whether this declaration was an element of a capture list.
unsigned IsCaptureList : 1;

/// \brief Whether this vardecl has an initial value bound to it in a way
/// that isn't represented in the AST with an initializer in the pattern
/// binding. This happens in cases like "for i in ...", switch cases, etc.
unsigned HasNonPatternBindingInit : 1;

/// \brief Whether this is a property used in expressions in the debugger.
/// It is up to the debugger to instruct SIL how to access this variable.
unsigned IsDebuggerVar : 1;
Expand All @@ -306,9 +309,9 @@ class alignas(1 << DeclAlignInBits) Decl {
/// a.storage for lazy var a is a decl that cannot be accessed.
unsigned IsUserAccessible : 1;
};
enum { NumVarDeclBits = NumAbstractStorageDeclBits + 5 };
enum { NumVarDeclBits = NumAbstractStorageDeclBits + 6 };
static_assert(NumVarDeclBits <= 32, "fits in an unsigned");

class EnumElementDeclBitfields {
friend class EnumElementDecl;
unsigned : NumValueDeclBits;
Expand Down Expand Up @@ -4074,13 +4077,14 @@ class VarDecl : public AbstractStorageDecl {
protected:
llvm::PointerUnion<PatternBindingDecl*, Stmt*> ParentPattern;

VarDecl(DeclKind Kind, bool IsStatic, bool IsLet, SourceLoc NameLoc,
Identifier Name, Type Ty, DeclContext *DC)
: AbstractStorageDecl(Kind, DC, Name, NameLoc)
VarDecl(DeclKind Kind, bool IsStatic, bool IsLet, bool IsCaptureList,
SourceLoc NameLoc, Identifier Name, Type Ty, DeclContext *DC)
: AbstractStorageDecl(Kind, DC, Name, NameLoc)
{
VarDeclBits.IsUserAccessible = true;
VarDeclBits.IsStatic = IsStatic;
VarDeclBits.IsLet = IsLet;
VarDeclBits.IsCaptureList = IsCaptureList;
VarDeclBits.IsDebuggerVar = false;
VarDeclBits.HasNonPatternBindingInit = false;
setType(Ty);
Expand All @@ -4092,9 +4096,10 @@ class VarDecl : public AbstractStorageDecl {
Type typeInContext;

public:
VarDecl(bool IsStatic, bool IsLet, SourceLoc NameLoc, Identifier Name,
Type Ty, DeclContext *DC)
: VarDecl(DeclKind::Var, IsStatic, IsLet, NameLoc, Name, Ty, DC) { }
VarDecl(bool IsStatic, bool IsLet, bool IsCaptureList, SourceLoc NameLoc,
Identifier Name, Type Ty, DeclContext *DC)
: VarDecl(DeclKind::Var, IsStatic, IsLet, IsCaptureList, NameLoc, Name, Ty,
DC) {}

SourceRange getSourceRange() const;

Expand All @@ -4105,7 +4110,7 @@ class VarDecl : public AbstractStorageDecl {
bool isUserAccessible() const {
return VarDeclBits.IsUserAccessible;
}

TypeLoc &getTypeLoc() { return typeLoc; }
TypeLoc getTypeLoc() const { return typeLoc; }

Expand Down Expand Up @@ -4185,7 +4190,7 @@ class VarDecl : public AbstractStorageDecl {
return PBD->getPatternEntryForVarDecl(this).getInit();
return nullptr;
}

VarDecl *getOverriddenDecl() const {
return cast_or_null<VarDecl>(AbstractStorageDecl::getOverriddenDecl());
}
Expand All @@ -4204,6 +4209,9 @@ class VarDecl : public AbstractStorageDecl {
bool isLet() const { return VarDeclBits.IsLet; }
void setLet(bool IsLet) { VarDeclBits.IsLet = IsLet; }

/// Is this an element in a capture list?
bool isCaptureList() const { return VarDeclBits.IsCaptureList; }

/// Return true if this vardecl has an initial value bound to it in a way
/// that isn't represented in the AST with an initializer in the pattern
/// binding. This happens in cases like "for i in ...", switch cases, etc.
Expand Down
15 changes: 9 additions & 6 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3743,7 +3743,10 @@ void VarDecl::emitLetToVarNoteIfSimple(DeclContext *UseDC) const {

// Besides self, don't suggest mutability for explicit function parameters.
if (isa<ParamDecl>(this)) return;


// Don't suggest any fixes for capture list elements.
if (isCaptureList()) return;

// If this is a normal variable definition, then we can change 'let' to 'var'.
// We even are willing to suggest this for multi-variable binding, like
// "let (a,b) = "
Expand All @@ -3764,18 +3767,18 @@ ParamDecl::ParamDecl(bool isLet,
SourceLoc letVarInOutLoc, SourceLoc argumentNameLoc,
Identifier argumentName, SourceLoc parameterNameLoc,
Identifier parameterName, Type ty, DeclContext *dc)
: VarDecl(DeclKind::Param, /*IsStatic=*/false, isLet, parameterNameLoc,
parameterName, ty, dc),
: VarDecl(DeclKind::Param, /*IsStatic*/false, /*IsLet*/isLet,
/*IsCaptureList*/false, parameterNameLoc, parameterName, ty, dc),
ArgumentName(argumentName), ArgumentNameLoc(argumentNameLoc),
LetVarInOutLoc(letVarInOutLoc) {
}

/// Clone constructor, allocates a new ParamDecl identical to the first.
/// Intentionally not defined as a copy constructor to avoid accidental copies.
ParamDecl::ParamDecl(ParamDecl *PD)
: VarDecl(DeclKind::Param, /*IsStatic=*/false, PD->isLet(), PD->getNameLoc(),
PD->getName(), PD->hasType() ? PD->getType() : Type(),
PD->getDeclContext()),
: VarDecl(DeclKind::Param, /*IsStatic*/false, /*IsLet*/PD->isLet(),
/*IsCaptureList*/false, PD->getNameLoc(), PD->getName(),
PD->hasType() ? PD->getType() : Type(), PD->getDeclContext()),
ArgumentName(PD->getArgumentName()),
ArgumentNameLoc(PD->getArgumentNameLoc()),
LetVarInOutLoc(PD->getLetVarInOutLoc()),
Expand Down
57 changes: 34 additions & 23 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,10 @@ createVarWithPattern(ASTContext &cxt, DeclContext *dc, Identifier name, Type ty,
Accessibility setterAccessibility) {
// Create a variable to store the underlying value.
auto var = new (cxt) VarDecl(
/*static*/ false,
/*IsLet*/ isLet, SourceLoc(), name, ty, dc);
/*IsStatic*/false,
/*IsLet*/isLet,
/*IsCaptureList*/false,
SourceLoc(), name, ty, dc);
if (isImplicit)
var->setImplicit();
var->setInterfaceType(ty);
Expand Down Expand Up @@ -1191,8 +1193,8 @@ static void makeStructRawValuedWithBridge(
//
// Create a computed value variable
auto computedVar = new (cxt) VarDecl(
/*static*/ false,
/*IsLet*/ false, SourceLoc(), computedVarName, bridgedType, structDecl);
/*IsStatic*/false, /*IsLet*/false, /*IsCaptureList*/false,
SourceLoc(), computedVarName, bridgedType, structDecl);
computedVar->setInterfaceType(bridgedType);
computedVar->setImplicit();
computedVar->setAccessibility(Accessibility::Public);
Expand Down Expand Up @@ -1496,8 +1498,8 @@ static bool addErrorDomain(NominalTypeDecl *swiftDecl,

// Make the property decl
auto errorDomainPropertyDecl = new (C) VarDecl(
isStatic,
/*IsLet=*/false, SourceLoc(), C.Id_nsErrorDomain, stringTy, swiftDecl);
/*IsStatic*/isStatic, /*IsLet*/false, /*IsCaptureList*/false,
SourceLoc(), C.Id_nsErrorDomain, stringTy, swiftDecl);
errorDomainPropertyDecl->setInterfaceType(stringTy);
errorDomainPropertyDecl->setAccessibility(Accessibility::Public);

Expand Down Expand Up @@ -2195,7 +2197,8 @@ namespace {
// Create the _nsError member.
// public let _nsError: NSError
auto nsErrorType = nsErrorDecl->getDeclaredInterfaceType();
auto nsErrorProp = new (C) VarDecl(/*static*/ false, /*IsLet*/ true,
auto nsErrorProp = new (C) VarDecl(/*IsStatic*/false, /*IsLet*/true,
/*IsCaptureList*/false,
loc, C.Id_nsError, nsErrorType,
errorWrapper);
nsErrorProp->setImplicit();
Expand Down Expand Up @@ -2261,10 +2264,10 @@ namespace {
auto rawValueConstructor = makeEnumRawValueConstructor(Impl, enumDecl);

auto varName = C.Id_rawValue;
auto rawValue = new (C) VarDecl(/*static*/ false,
/*IsLet*/ false,
SourceLoc(), varName,
underlyingType, enumDecl);
auto rawValue = new (C) VarDecl(/*IsStatic*/false, /*IsLet*/ false,
/*IsCaptureList*/false,
SourceLoc(), varName, underlyingType,
enumDecl);
rawValue->setImplicit();
rawValue->setAccessibility(Accessibility::Public);
rawValue->setSetterAccessibility(Accessibility::Private);
Expand Down Expand Up @@ -2789,7 +2792,8 @@ namespace {
// Map this indirect field to a Swift variable.
auto result = Impl.createDeclWithClangNode<VarDecl>(decl,
Accessibility::Public,
/*static*/ false, /*IsLet*/ false,
/*IsStatic*/false, /*IsLet*/ false,
/*IsCaptureList*/false,
Impl.importSourceLoc(decl->getLocStart()),
name, type, dc);
result->setInterfaceType(type);
Expand Down Expand Up @@ -2982,7 +2986,8 @@ namespace {

auto result =
Impl.createDeclWithClangNode<VarDecl>(decl, Accessibility::Public,
/*static*/ false, /*IsLet*/ false,
/*IsStatic*/ false, /*IsLet*/ false,
/*IsCaptureList*/false,
Impl.importSourceLoc(decl->getLocation()),
name, type, dc);
result->setInterfaceType(type);
Expand Down Expand Up @@ -3055,8 +3060,10 @@ namespace {
isStatic = true;

auto result = Impl.createDeclWithClangNode<VarDecl>(decl,
Accessibility::Public, isStatic,
Impl.shouldImportGlobalAsLet(decl->getType()),
Accessibility::Public,
/*IsStatic*/isStatic,
/*IsLet*/Impl.shouldImportGlobalAsLet(decl->getType()),
/*IsCaptureList*/false,
Impl.importSourceLoc(decl->getLocation()),
name, type, dc);
result->setInterfaceType(type);
Expand Down Expand Up @@ -4115,8 +4122,8 @@ namespace {

auto result = Impl.createDeclWithClangNode<VarDecl>(decl,
getOverridableAccessibility(dc),
decl->isClassProperty(), /*IsLet*/ false,
Impl.importSourceLoc(decl->getLocation()),
/*IsStatic*/decl->isClassProperty(), /*IsLet*/false,
/*IsCaptureList*/false, Impl.importSourceLoc(decl->getLocation()),
name, type, dc);
result->setInterfaceType(dc->mapTypeOutOfContext(type));

Expand Down Expand Up @@ -4989,8 +4996,8 @@ SwiftDeclConverter::getImplicitProperty(ImportedName importedName,
return nullptr;

auto property = Impl.createDeclWithClangNode<VarDecl>(
getter, Accessibility::Public, isStatic,
/*isLet=*/false, SourceLoc(), propertyName, swiftPropertyType, dc);
getter, Accessibility::Public, /*IsStatic*/isStatic, /*isLet*/false,
/*IsCaptureList*/false, SourceLoc(), propertyName, swiftPropertyType, dc);
property->setInterfaceType(swiftPropertyType);

// Note that we've formed this property.
Expand Down Expand Up @@ -7041,11 +7048,13 @@ ClangImporter::Implementation::createConstant(Identifier name, DeclContext *dc,
VarDecl *var = nullptr;
if (ClangN) {
var = createDeclWithClangNode<VarDecl>(ClangN, Accessibility::Public,
isStatic, /*IsLet*/ false,
SourceLoc(), name, type, dc);
/*IsStatic*/isStatic, /*IsLet*/false,
/*IsCaptureList*/false, SourceLoc(),
name, type, dc);
} else {
var = new (SwiftContext)
VarDecl(isStatic, /*IsLet*/ false, SourceLoc(), name, type, dc);
VarDecl(/*IsStatic*/isStatic, /*IsLet*/false, /*IsCaptureList*/false,
SourceLoc(), name, type, dc);
}

var->setInterfaceType(type);
Expand Down Expand Up @@ -7149,7 +7158,9 @@ createUnavailableDecl(Identifier name, DeclContext *dc, Type type,

// Create a new VarDecl with dummy type.
auto var = createDeclWithClangNode<VarDecl>(ClangN, Accessibility::Public,
isStatic, /*IsLet*/ false,
/*IsStatic*/isStatic,
/*IsLet*/false,
/*IsCaptureList*/false,
SourceLoc(), name, type, dc);
var->setInterfaceType(type);
markUnavailable(var, UnavailableMessage);
Expand Down
12 changes: 6 additions & 6 deletions lib/Parse/ParseExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2154,23 +2154,23 @@ parseClosureSignatureIfPresent(SmallVectorImpl<CaptureListEntry> &captureList,
// the initializer expression is evaluated before the closure is formed.
auto *VD = new (Context) VarDecl(/*isStatic*/false,
/*isLet*/ownershipKind !=Ownership::Weak,
/*isCaptureList*/true,
nameLoc, name, Type(), CurDeclContext);

// Attributes.
if (ownershipKind != Ownership::Strong)
VD->getAttrs().add(new (Context) OwnershipAttr(ownershipKind));

auto pattern = new (Context) NamedPattern(VD, /*implicit*/true);

auto *PBD = PatternBindingDecl::create(Context, /*staticloc*/SourceLoc(),
StaticSpellingKind::None,
nameLoc, pattern, initializer,
CurDeclContext);




captureList.push_back(CaptureListEntry(VD, PBD));
} while (consumeIf(tok::comma));

// The capture list needs to be closed off with a ']'.
if (!consumeIf(tok::r_square)) {
diagnose(Tok, diag::expected_capture_list_end_rsquare);
Expand Down
5 changes: 3 additions & 2 deletions lib/Parse/ParsePattern.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -819,8 +819,9 @@ ParserResult<Pattern> Parser::parsePattern() {

Pattern *Parser::createBindingFromPattern(SourceLoc loc, Identifier name,
bool isLet) {
auto var = new (Context) VarDecl(/*static*/ false, /*IsLet*/ isLet,
loc, name, Type(), CurDeclContext);
auto var = new (Context) VarDecl(/*IsStatic*/false, /*IsLet*/isLet,
/*IsCaptureList*/false, loc, name, Type(),
CurDeclContext);
return new (Context) NamedPattern(var);
}

Expand Down
6 changes: 3 additions & 3 deletions lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -914,9 +914,9 @@ static void parseGuardedPattern(Parser &P, GuardedPattern &result,
P.Tok.isAny(tok::l_brace, tok::kw_where)) {
auto loc = P.Tok.getLoc();
auto errorName = P.Context.Id_error;
auto var = new (P.Context) VarDecl(/*static*/ false, /*IsLet*/true,
loc, errorName, Type(),
P.CurDeclContext);
auto var = new (P.Context) VarDecl(/*IsStatic*/false, /*IsLet*/true,
/*IsCaptureList*/false, loc, errorName,
Type(), P.CurDeclContext);
var->setImplicit();
auto namePattern = new (P.Context) NamedPattern(var);
auto varPattern = new (P.Context) VarPattern(loc, /*isLet*/true,
Expand Down
8 changes: 5 additions & 3 deletions lib/Sema/CSDiag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -670,8 +670,10 @@ static void diagnoseSubElementFailure(Expr *destExpr,
std::string message = "'";
message += VD->getName().str().str();
message += "'";

if (VD->isImplicit())

if (VD->isCaptureList())
message += " is an immutable capture";
else if (VD->isImplicit())
message += " is immutable";
else if (VD->isLet())
message += " is a 'let' constant";
Expand All @@ -684,7 +686,7 @@ static void diagnoseSubElementFailure(Expr *destExpr,
}
TC.diagnose(loc, diagID, message)
.highlight(immInfo.first->getSourceRange());

// If this is a simple variable marked with a 'let', emit a note to fixit
// hint it to 'var'.
VD->emitLetToVarNoteIfSimple(CS.DC);
Expand Down
Loading