mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Sema: Bring back 'assignment' as an infix operator modifier.
We need it to decide whether to admit infix operators into an optional chain, such as 'x? = 0' or 'x?.y += 2'. Swift SVN r20295
This commit is contained in:
@@ -74,11 +74,15 @@ class InfixData {
|
||||
unsigned Precedence : 8;
|
||||
|
||||
/// Zero if invalid, or else an Associativity+1.
|
||||
unsigned InvalidOrAssoc : 8;
|
||||
unsigned InvalidOrAssoc : 2;
|
||||
|
||||
unsigned Assignment : 1;
|
||||
|
||||
public:
|
||||
InfixData() : Precedence(0), InvalidOrAssoc(0) {}
|
||||
InfixData(unsigned char prec, Associativity assoc)
|
||||
: Precedence(prec), InvalidOrAssoc(unsigned(assoc) + 1) {}
|
||||
InfixData() : Precedence(0), InvalidOrAssoc(0), Assignment(0) {}
|
||||
InfixData(unsigned char prec, Associativity assoc, bool isAssignment)
|
||||
: Precedence(prec), InvalidOrAssoc(unsigned(assoc) + 1),
|
||||
Assignment((unsigned)isAssignment) {}
|
||||
|
||||
bool isValid() const { return InvalidOrAssoc != 0; }
|
||||
|
||||
@@ -101,9 +105,15 @@ public:
|
||||
return Precedence;
|
||||
}
|
||||
|
||||
bool isAssignment() const {
|
||||
assert(isValid());
|
||||
return (bool)Assignment;
|
||||
}
|
||||
|
||||
friend bool operator==(InfixData L, InfixData R) {
|
||||
return L.Precedence == R.Precedence
|
||||
&& L.InvalidOrAssoc == R.InvalidOrAssoc;
|
||||
&& L.InvalidOrAssoc == R.InvalidOrAssoc
|
||||
&& L.Assignment == R.Assignment;
|
||||
}
|
||||
friend bool operator!=(InfixData L, InfixData R) {
|
||||
return !operator==(L, R);
|
||||
|
||||
@@ -520,10 +520,12 @@ class alignas(8) Decl {
|
||||
|
||||
unsigned Associativity : 2;
|
||||
unsigned Precedence : 8;
|
||||
unsigned Assignment : 1;
|
||||
unsigned IsAssocImplicit : 1;
|
||||
unsigned IsPrecedenceImplicit : 1;
|
||||
unsigned IsAssignmentImplicit : 1;
|
||||
};
|
||||
enum { NumInfixOperatorDeclBits = NumDeclBits + 12 };
|
||||
enum { NumInfixOperatorDeclBits = NumDeclBits + 14 };
|
||||
static_assert(NumInfixOperatorDeclBits <= 32, "fits in an unsigned");
|
||||
|
||||
class ImportDeclBitfields {
|
||||
@@ -4484,7 +4486,8 @@ public:
|
||||
/// \endcode
|
||||
class InfixOperatorDecl : public OperatorDecl {
|
||||
SourceLoc AssociativityLoc, AssociativityValueLoc,
|
||||
PrecedenceLoc, PrecedenceValueLoc;
|
||||
PrecedenceLoc, PrecedenceValueLoc,
|
||||
AssignmentLoc;
|
||||
|
||||
public:
|
||||
InfixOperatorDecl(DeclContext *DC,
|
||||
@@ -4498,6 +4501,8 @@ public:
|
||||
bool IsPrecedenceImplicit,
|
||||
SourceLoc PrecedenceLoc,
|
||||
SourceLoc PrecedenceValueLoc,
|
||||
bool IsAssignmentImplicit,
|
||||
SourceLoc AssignmentLoc,
|
||||
SourceLoc RBraceLoc,
|
||||
InfixData InfixData)
|
||||
: OperatorDecl(DeclKind::InfixOperator, DC,
|
||||
@@ -4509,7 +4514,8 @@ public:
|
||||
AssociativityLoc(AssociativityLoc),
|
||||
AssociativityValueLoc(AssociativityValueLoc),
|
||||
PrecedenceLoc(PrecedenceLoc),
|
||||
PrecedenceValueLoc(PrecedenceValueLoc) {
|
||||
PrecedenceValueLoc(PrecedenceValueLoc),
|
||||
AssignmentLoc(AssignmentLoc) {
|
||||
if (!InfixData.isValid()) {
|
||||
setInvalid();
|
||||
} else {
|
||||
@@ -4520,8 +4526,11 @@ public:
|
||||
InfixOperatorDeclBits.Precedence = InfixData.getPrecedence();
|
||||
InfixOperatorDeclBits.Associativity =
|
||||
static_cast<unsigned>(InfixData.getAssociativity());
|
||||
InfixOperatorDeclBits.Assignment =
|
||||
unsigned(InfixData.isAssignment());
|
||||
InfixOperatorDeclBits.IsPrecedenceImplicit = IsPrecedenceImplicit;
|
||||
InfixOperatorDeclBits.IsAssocImplicit = IsAssocImplicit;
|
||||
InfixOperatorDeclBits.IsAssignmentImplicit = IsAssignmentImplicit;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4529,6 +4538,7 @@ public:
|
||||
SourceLoc getAssociativityValueLoc() const { return AssociativityValueLoc; }
|
||||
SourceLoc getPrecedenceLoc() const { return PrecedenceLoc; }
|
||||
SourceLoc getPrecedenceValueLoc() const { return PrecedenceValueLoc; }
|
||||
SourceLoc getAssignmentLoc() const { return AssignmentLoc; }
|
||||
|
||||
unsigned getPrecedence() const {
|
||||
return InfixOperatorDeclBits.Precedence;
|
||||
@@ -4538,10 +4548,14 @@ public:
|
||||
return Associativity(InfixOperatorDeclBits.Associativity);
|
||||
}
|
||||
|
||||
bool isAssignment() const {
|
||||
return InfixOperatorDeclBits.Assignment;
|
||||
}
|
||||
|
||||
InfixData getInfixData() const {
|
||||
if (isInvalid())
|
||||
return InfixData();
|
||||
return InfixData(getPrecedence(), getAssociativity());
|
||||
return InfixData(getPrecedence(), getAssociativity(), isAssignment());
|
||||
}
|
||||
|
||||
bool isAssociativityImplicit() const {
|
||||
@@ -4550,6 +4564,9 @@ public:
|
||||
bool isPrecedenceImplicit() const {
|
||||
return InfixOperatorDeclBits.IsPrecedenceImplicit;
|
||||
}
|
||||
bool isAssignmentImplicit() const {
|
||||
return InfixOperatorDeclBits.IsAssignmentImplicit;
|
||||
}
|
||||
|
||||
/// True if this decl's attributes conflict with those declared by another
|
||||
/// operator.
|
||||
|
||||
@@ -362,6 +362,8 @@ ERROR(unknown_infix_operator_associativity,decl_parsing,none,
|
||||
"'%0' is not a valid infix operator associativity; must be 'none', 'left', or 'right'", (StringRef))
|
||||
ERROR(operator_precedence_redeclared,decl_parsing,none,
|
||||
"'precedence' for infix operator declared multiple times", ())
|
||||
ERROR(operator_assignment_redeclared,decl_parsing,none,
|
||||
"'assignment' for infix operator declared multiple times", ())
|
||||
ERROR(expected_infix_operator_precedence,decl_parsing,none,
|
||||
"expected integer literal after 'precedence' in 'operator' declaration body", ())
|
||||
ERROR(invalid_infix_operator_precedence,decl_parsing,none,
|
||||
|
||||
@@ -782,8 +782,10 @@ namespace decls_block {
|
||||
DeclIDField, // context decl
|
||||
AssociativityField,
|
||||
BCFixed<8>, // precedence
|
||||
BCFixed<1>, // assignment
|
||||
BCFixed<1>, // IsAccocImplicit flag
|
||||
BCFixed<1> // IsPrecedenceImplicit flag
|
||||
BCFixed<1>, // IsPrecedenceImplicit flag
|
||||
BCFixed<1> // IsAssignmentImplicit flag
|
||||
>;
|
||||
|
||||
using EnumElementLayout = BCRecordLayout<
|
||||
|
||||
@@ -1411,6 +1411,14 @@ void PrintAST::visitInfixOperatorDecl(InfixOperatorDecl *decl) {
|
||||
Printer << "precedence " << decl->getPrecedence();
|
||||
Printer.printNewline();
|
||||
}
|
||||
if (!decl->isAssignmentImplicit()) {
|
||||
indent();
|
||||
if (decl->isAssignment())
|
||||
Printer << "assignment";
|
||||
else
|
||||
Printer << "/* not assignment */";
|
||||
Printer.printNewline();
|
||||
}
|
||||
}
|
||||
indent();
|
||||
Printer << "}";
|
||||
|
||||
@@ -3988,12 +3988,14 @@ Parser::parseDeclInfixOperator(SourceLoc OperatorLoc, Identifier Name,
|
||||
SourceLoc LBraceLoc = consumeToken(tok::l_brace);
|
||||
|
||||
// Initialize InfixData with default attributes:
|
||||
// precedence 100, associativity none
|
||||
// precedence 100, associativity none, non-assignment
|
||||
unsigned char precedence = 100;
|
||||
Associativity associativity = Associativity::None;
|
||||
bool assignment = false;
|
||||
|
||||
SourceLoc AssociativityLoc, AssociativityValueLoc,
|
||||
PrecedenceLoc, PrecedenceValueLoc;
|
||||
PrecedenceLoc, PrecedenceValueLoc,
|
||||
AssignmentLoc;
|
||||
|
||||
while (!Tok.is(tok::r_brace)) {
|
||||
if (!Tok.is(tok::identifier)) {
|
||||
@@ -4052,6 +4054,17 @@ Parser::parseDeclInfixOperator(SourceLoc OperatorLoc, Identifier Name,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Tok.getText().equals("assignment")) {
|
||||
if (AssignmentLoc.isValid()) {
|
||||
diagnose(Tok, diag::operator_assignment_redeclared);
|
||||
skipUntilDeclRBrace();
|
||||
return nullptr;
|
||||
}
|
||||
AssignmentLoc = consumeToken();
|
||||
assignment = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
diagnose(Tok, diag::unknown_infix_operator_attribute, Tok.getText());
|
||||
skipUntilDeclRBrace();
|
||||
return nullptr;
|
||||
@@ -4063,8 +4076,10 @@ Parser::parseDeclInfixOperator(SourceLoc OperatorLoc, Identifier Name,
|
||||
InfixOperatorDecl(CurDeclContext, OperatorLoc, Name, NameLoc, LBraceLoc,
|
||||
AssociativityLoc.isInvalid(), AssociativityLoc,
|
||||
AssociativityValueLoc, PrecedenceLoc.isInvalid(),
|
||||
PrecedenceLoc, PrecedenceValueLoc, RBraceLoc,
|
||||
InfixData(precedence, associativity));
|
||||
PrecedenceLoc, PrecedenceValueLoc,
|
||||
AssignmentLoc.isInvalid(), AssignmentLoc,
|
||||
RBraceLoc,
|
||||
InfixData(precedence, associativity, assignment));
|
||||
Res->getAttrs() = Attributes;
|
||||
return makeParserResult(Res);
|
||||
}
|
||||
|
||||
@@ -154,21 +154,24 @@ static InfixData getInfixData(TypeChecker &TC, DeclContext *DC, Expr *E) {
|
||||
assert(!ifExpr->isFolded() && "already folded if expr in sequence?!");
|
||||
(void)ifExpr;
|
||||
return InfixData(IntrinsicPrecedences::IfExpr,
|
||||
Associativity::Right);
|
||||
Associativity::Right,
|
||||
/*assignment*/ false);
|
||||
|
||||
} else if (auto *assign = dyn_cast<AssignExpr>(E)) {
|
||||
// Assignment has fixed precedence.
|
||||
assert(!assign->isFolded() && "already folded assign expr in sequence?!");
|
||||
(void)assign;
|
||||
return InfixData(IntrinsicPrecedences::AssignExpr,
|
||||
Associativity::Right);
|
||||
Associativity::Right,
|
||||
/*assignment*/ true);
|
||||
|
||||
} else if (auto *as = dyn_cast<ExplicitCastExpr>(E)) {
|
||||
// 'as' and 'is' casts have fixed precedence.
|
||||
assert(!as->isFolded() && "already folded 'as' expr in sequence?!");
|
||||
(void)as;
|
||||
return InfixData(IntrinsicPrecedences::ExplicitCastExpr,
|
||||
Associativity::None);
|
||||
Associativity::None,
|
||||
/*assignment*/ false);
|
||||
|
||||
} else if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
|
||||
SourceFile *SF = DC->getParentSourceFile();
|
||||
@@ -185,7 +188,8 @@ static InfixData getInfixData(TypeChecker &TC, DeclContext *DC, Expr *E) {
|
||||
|
||||
TC.diagnose(E->getLoc(), diag::unknown_binop);
|
||||
// Recover with an infinite-precedence left-associative operator.
|
||||
return InfixData((unsigned char)~0U, Associativity::Left);
|
||||
return InfixData((unsigned char)~0U, Associativity::Left,
|
||||
/*assignment*/ false);
|
||||
}
|
||||
|
||||
static Expr *makeBinOp(TypeChecker &TC, Expr *Op, Expr *LHS, Expr *RHS) {
|
||||
|
||||
@@ -2227,13 +2227,17 @@ Decl *ModuleFile::getDecl(DeclID DID, Optional<DeclContext *> ForcedContext) {
|
||||
DeclID contextID;
|
||||
uint8_t rawAssociativity;
|
||||
unsigned precedence;
|
||||
bool isAssignment;
|
||||
bool isAssocImplicit;
|
||||
bool isPrecedenceImplicit;
|
||||
bool isAssignmentImplicit;
|
||||
|
||||
decls_block::InfixOperatorLayout::readRecord(scratch, nameID, contextID,
|
||||
rawAssociativity, precedence,
|
||||
isAssignment,
|
||||
isAssocImplicit,
|
||||
isPrecedenceImplicit);
|
||||
isPrecedenceImplicit,
|
||||
isAssignmentImplicit);
|
||||
|
||||
auto associativity = getActualAssociativity(rawAssociativity);
|
||||
if (!associativity.hasValue()) {
|
||||
@@ -2241,7 +2245,8 @@ Decl *ModuleFile::getDecl(DeclID DID, Optional<DeclContext *> ForcedContext) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
InfixData infixData(precedence, associativity.getValue());
|
||||
InfixData infixData(precedence, associativity.getValue(),
|
||||
isAssignment);
|
||||
|
||||
declOrOffset = new (ctx) InfixOperatorDecl(getDeclContext(contextID),
|
||||
SourceLoc(),
|
||||
@@ -2251,6 +2256,8 @@ Decl *ModuleFile::getDecl(DeclID DID, Optional<DeclContext *> ForcedContext) {
|
||||
SourceLoc(), SourceLoc(),
|
||||
isPrecedenceImplicit,
|
||||
SourceLoc(), SourceLoc(),
|
||||
isAssignmentImplicit,
|
||||
SourceLoc(),
|
||||
SourceLoc(), infixData);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1501,8 +1501,10 @@ void Serializer::writeDecl(const Decl *D) {
|
||||
addDeclRef(DC),
|
||||
associativity,
|
||||
op->getPrecedence(),
|
||||
op->isAssignment(),
|
||||
op->isAssociativityImplicit(),
|
||||
op->isPrecedenceImplicit());
|
||||
op->isPrecedenceImplicit(),
|
||||
op->isAssignmentImplicit());
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -1002,6 +1002,7 @@ func %%%(inout lhs: d2601_TestAssignment, rhs: d2601_TestAssignment) -> Int {
|
||||
// PASS_2500-LABEL: {{^}}infix operator %%% {
|
||||
// PASS_2500-NOT: associativity
|
||||
// PASS_2500-NOT: precedence
|
||||
// PASS_2500-NOT: assignment
|
||||
// PASS_2500: {{^}}func %%%(inout lhs: d2601_TestAssignment, rhs: d2601_TestAssignment) -> Int{{$}}
|
||||
|
||||
infix operator %%< {
|
||||
@@ -1010,6 +1011,7 @@ infix operator %%< {
|
||||
// PASS_2500-NEXT: {{^}} associativity left{{$}}
|
||||
precedence 47
|
||||
// PASS_2500-NEXT: {{^}} precedence 47{{$}}
|
||||
// PASS_2500-NOT: assignment
|
||||
}
|
||||
|
||||
infix operator %%> {
|
||||
@@ -1017,12 +1019,15 @@ infix operator %%> {
|
||||
associativity right
|
||||
// PASS_2500-NEXT: {{^}} associativity right{{$}}
|
||||
// PASS_2500-NOT: precedence
|
||||
// PASS_2500-NOT: assignment
|
||||
}
|
||||
|
||||
infix operator %%<> {
|
||||
// PASS_2500-LABEL: {{^}}infix operator %%<> {{{$}}
|
||||
precedence 47
|
||||
assignment
|
||||
// PASS_2500-NEXT: {{^}} precedence 47{{$}}
|
||||
// PASS_2500-NEXT: {{^}} assignment{{$}}
|
||||
// PASS_2500-NOT: associativity
|
||||
}
|
||||
// PASS_2500: {{^}}}{{$}}
|
||||
|
||||
@@ -74,3 +74,11 @@ class Foo {
|
||||
// rdar://14690497
|
||||
infix operator ~> { precedence 99999 } // expected-error {{'precedence' must be in the range of 0 to 255}}
|
||||
|
||||
infix operator ->= {
|
||||
assignment
|
||||
}
|
||||
|
||||
infix operator ->== {
|
||||
assignment assignment // expected-error{{'assignment' for infix operator declared multiple}}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ infix operator -* {
|
||||
infix operator *-* {
|
||||
associativity none
|
||||
precedence 10
|
||||
assignment
|
||||
}
|
||||
|
||||
prefix public func ~~~(x: Bool) -> () {}
|
||||
|
||||
Reference in New Issue
Block a user