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:
Joe Groff
2014-07-22 05:16:01 +00:00
parent 710b35b396
commit 7feeecfd21
12 changed files with 102 additions and 21 deletions

View File

@@ -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);

View File

@@ -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.

View File

@@ -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,

View File

@@ -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<

View File

@@ -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 << "}";

View File

@@ -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);
}

View File

@@ -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) {

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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: {{^}}}{{$}}

View File

@@ -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}}
}

View File

@@ -14,6 +14,7 @@ infix operator -* {
infix operator *-* {
associativity none
precedence 10
assignment
}
prefix public func ~~~(x: Bool) -> () {}