Skip to content

[reference-binding] Parser support for inout bindings #64012

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 5 commits into from
Mar 2, 2023
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
7 changes: 4 additions & 3 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -388,9 +388,9 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
IsStatic : 1
);

SWIFT_INLINE_BITFIELD(VarDecl, AbstractStorageDecl, 1+1+1+1+1+1,
SWIFT_INLINE_BITFIELD(VarDecl, AbstractStorageDecl, 2+1+1+1+1+1,
/// Encodes whether this is a 'let' binding.
Introducer : 1,
Introducer : 2,

/// Whether this declaration captures the 'self' param under the same name.
IsSelfParamCapture : 1,
Expand Down Expand Up @@ -5498,7 +5498,8 @@ class VarDecl : public AbstractStorageDecl {
public:
enum class Introducer : uint8_t {
Let = 0,
Var = 1
Var = 1,
InOut = 2,
};

protected:
Expand Down
4 changes: 2 additions & 2 deletions include/swift/AST/DiagnosticsParse.def
Original file line number Diff line number Diff line change
Expand Up @@ -964,8 +964,8 @@ ERROR(no_default_arg_closure,none,
ERROR(no_default_arg_curried,none,
"default arguments are not allowed in curried parameter lists", ())
ERROR(var_pattern_in_var,none,
"'%select{var|let}0' cannot appear nested inside another 'var' or "
"'let' pattern", (unsigned))
"'%select{let|inout|var}0' cannot appear nested inside another 'var', "
"'let', or 'inout' pattern", (unsigned))
ERROR(extra_var_in_multiple_pattern_list,none,
"%0 must be bound in every pattern", (Identifier))
ERROR(let_pattern_in_immutable_context,none,
Expand Down
39 changes: 31 additions & 8 deletions include/swift/AST/Pattern.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, PatternKind kind);
/// Pattern - Base class for all patterns in Swift.
class alignas(8) Pattern : public ASTAllocated<Pattern> {
protected:
// clang-format off
union { uint64_t OpaqueBits;

SWIFT_INLINE_BITFIELD_BASE(Pattern, bitmax(NumPatternKindBits,8)+1+1,
Expand All @@ -74,16 +75,17 @@ class alignas(8) Pattern : public ASTAllocated<Pattern> {
Value : 1
);

SWIFT_INLINE_BITFIELD(BindingPattern, Pattern, 1,
/// True if this is a let pattern, false if a var pattern.
IsLet : 1
SWIFT_INLINE_BITFIELD(BindingPattern, Pattern, 2,
/// Corresponds to VarDecl::Introducer
Introducer : 2
);

SWIFT_INLINE_BITFIELD(AnyPattern, Pattern, 1,
/// True if this is an "async let _" pattern.
IsAsyncLet : 1);

} Bits;
// clang-format on

Pattern(PatternKind kind) {
Bits.OpaqueBits = 0;
Expand Down Expand Up @@ -701,20 +703,41 @@ class ExprPattern : public Pattern {
class BindingPattern : public Pattern {
SourceLoc VarLoc;
Pattern *SubPattern;

public:
BindingPattern(SourceLoc loc, bool isLet, Pattern *sub)
BindingPattern(SourceLoc loc, VarDecl::Introducer introducer, Pattern *sub)
: Pattern(PatternKind::Binding), VarLoc(loc), SubPattern(sub) {
Bits.BindingPattern.IsLet = isLet;
setIntroducer(introducer);
}

VarDecl::Introducer getIntroducer() const {
return VarDecl::Introducer(Bits.BindingPattern.Introducer);
}

static BindingPattern *createImplicit(ASTContext &Ctx, bool isLet,
void setIntroducer(VarDecl::Introducer introducer) {
Bits.BindingPattern.Introducer = uint8_t(introducer);
}

static BindingPattern *createImplicit(ASTContext &Ctx,
VarDecl::Introducer introducer,
Pattern *sub) {
auto *VP = new (Ctx) BindingPattern(SourceLoc(), isLet, sub);
auto *VP = new (Ctx) BindingPattern(SourceLoc(), introducer, sub);
VP->setImplicit();
return VP;
}

bool isLet() const { return Bits.BindingPattern.IsLet; }
bool isLet() const { return getIntroducer() == VarDecl::Introducer::Let; }

StringRef getIntroducerStringRef() const {
switch (getIntroducer()) {
case VarDecl::Introducer::Let:
return "let";
case VarDecl::Introducer::Var:
return "var";
case VarDecl::Introducer::InOut:
return "inout";
}
}

SourceLoc getLoc() const { return VarLoc; }
SourceRange getSourceRange() const {
Expand Down
41 changes: 9 additions & 32 deletions include/swift/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,20 @@

#include "swift/AST/ASTContext.h"
#include "swift/AST/ASTNode.h"
#include "swift/AST/Expr.h"
#include "swift/AST/DiagnosticsParse.h"
#include "swift/AST/Expr.h"
#include "swift/AST/LayoutConstraint.h"
#include "swift/AST/ParseRequests.h"
#include "swift/AST/Pattern.h"
#include "swift/AST/Stmt.h"
#include "swift/Basic/OptionSet.h"
#include "swift/Config.h"
#include "swift/Parse/Lexer.h"
#include "swift/Parse/PersistentParserState.h"
#include "swift/Parse/Token.h"
#include "swift/Parse/ParserPosition.h"
#include "swift/Parse/ParserResult.h"
#include "swift/Config.h"
#include "swift/Parse/PatternBindingState.h"
#include "swift/Parse/PersistentParserState.h"
#include "swift/Parse/Token.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"

namespace llvm {
Expand Down Expand Up @@ -142,30 +143,7 @@ class Parser {

void recordTokenHash(StringRef token);

enum {
/// InVarOrLetPattern has this value when not parsing a pattern.
IVOLP_NotInVarOrLet,

/// InVarOrLetPattern has this value when we're in a matching pattern, but
/// not within a var/let pattern. In this phase, identifiers are references
/// to the enclosing scopes, not a variable binding.
IVOLP_InMatchingPattern,

/// InVarOrLetPattern has this value when parsing a pattern in which bound
/// variables are implicitly immutable, but allowed to be marked mutable by
/// using a 'var' pattern. This happens in for-each loop patterns.
IVOLP_ImplicitlyImmutable,

/// When InVarOrLetPattern has this value, bound variables are mutable, and
/// nested let/var patterns are not permitted. This happens when parsing a
/// 'var' decl or when parsing inside a 'var' pattern.
IVOLP_InVar,

/// When InVarOrLetPattern has this value, bound variables are immutable,and
/// nested let/var patterns are not permitted. This happens when parsing a
/// 'let' decl or when parsing inside a 'let' pattern.
IVOLP_InLet
} InVarOrLetPattern = IVOLP_NotInVarOrLet;
PatternBindingState InBindingPattern = PatternBindingState::NotInBinding;

/// Whether this context has an async attribute.
bool InPatternWithAsyncAttribute = false;
Expand Down Expand Up @@ -1608,10 +1586,9 @@ class Parser {
ParserResult<Pattern>
parseOptionalPatternTypeAnnotation(ParserResult<Pattern> P);
ParserResult<Pattern> parseMatchingPattern(bool isExprBasic);
ParserResult<Pattern> parseMatchingPatternAsLetOrVar(bool isLet,
SourceLoc VarLoc,
bool isExprBasic);

ParserResult<Pattern>
parseMatchingPatternAsBinding(PatternBindingState newState, SourceLoc VarLoc,
bool isExprBasic);

Pattern *createBindingFromPattern(SourceLoc loc, Identifier name,
VarDecl::Introducer introducer);
Expand Down
144 changes: 144 additions & 0 deletions include/swift/Parse/PatternBindingState.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
//===--- PatternBindingState.h --------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_PARSE_PATTERNBINDINGSTATE_H
#define SWIFT_PARSE_PATTERNBINDINGSTATE_H

#include "swift/AST/Decl.h"
#include "swift/Parse/Token.h"

namespace swift {

class Token;

struct PatternBindingState {
enum Kind {
/// InBindingPattern has this value when not parsing a pattern.
NotInBinding,

/// InBindingPattern has this value when we're in a matching pattern, but
/// not within a var/let pattern. In this phase, identifiers are references
/// to the enclosing scopes, not a variable binding.
InMatchingPattern,

/// InBindingPattern has this value when parsing a pattern in which bound
/// variables are implicitly immutable, but allowed to be marked mutable by
/// using a 'var' pattern. This happens in for-each loop patterns.
ImplicitlyImmutable,

/// When InBindingPattern has this value, bound variables are mutable, and
/// nested let/var patterns are not permitted. This happens when parsing a
/// 'var' decl or when parsing inside a 'var' pattern.
InVar,

/// When InBindingPattern has this value, bound variables are immutable,and
/// nested let/var patterns are not permitted. This happens when parsing a
/// 'let' decl or when parsing inside a 'let' pattern.
InLet,

/// When InBindingPattern has this value, bound variables are mutable, and
/// nested let/var/inout patterns are not permitted. This happens when
/// parsing an 'inout' decl or when parsing inside an 'inout' pattern.
InInOut,
};

Kind kind;

PatternBindingState(Kind kind) : kind(kind) {}

operator bool() const { return kind != Kind::NotInBinding; }

operator Kind() const { return kind; }

static Optional<PatternBindingState> get(StringRef str) {
auto kind = llvm::StringSwitch<Kind>(str)
.Case("let", Kind::InLet)
.Case("var", Kind::InVar)
.Case("inout", Kind::InInOut)
.Default(Kind::NotInBinding);
return PatternBindingState(kind);
}

/// Try to explicitly find the new pattern binding state from a token.
explicit PatternBindingState(Token tok) : kind(NotInBinding) {
switch (tok.getKind()) {
case tok::kw_let:
kind = InLet;
break;
case tok::kw_var:
kind = InVar;
break;
case tok::kw_inout:
kind = InInOut;
break;
default:
break;
}
}

/// Explicitly initialize from a VarDecl::Introducer.
explicit PatternBindingState(VarDecl::Introducer introducer)
: kind(NotInBinding) {
switch (introducer) {
case VarDecl::Introducer::Let:
kind = InLet;
break;
case VarDecl::Introducer::Var:
kind = InVar;
break;
case VarDecl::Introducer::InOut:
kind = InInOut;
break;
}
}

/// If there is a direct introducer associated with this pattern binding
/// state, return that. Return none otherwise.
Optional<VarDecl::Introducer> getIntroducer() const {
switch (kind) {
case Kind::NotInBinding:
case Kind::InMatchingPattern:
case Kind::ImplicitlyImmutable:
return None;
case Kind::InVar:
return VarDecl::Introducer::Var;
case Kind::InLet:
return VarDecl::Introducer::Let;
case Kind::InInOut:
return VarDecl::Introducer::InOut;
}
}

PatternBindingState
getPatternBindingStateForIntroducer(VarDecl::Introducer defaultValue) {
return PatternBindingState(getIntroducer().getValueOr(defaultValue));
}

Optional<unsigned> getSelectIndexForIntroducer() const {
switch (kind) {
case Kind::InLet:
return 0;
case Kind::InInOut:
return 1;
case Kind::InVar:
return 2;
default:
return None;
}
}

bool isLet() const { return kind == Kind::InLet; }
};

} // namespace swift

#endif
Loading