diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 181634d4bbf..1284062a695 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -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; } @@ -100,10 +104,16 @@ public: assert(isValid()); 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); diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index c17509c1d94..ae1d61881bc 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -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(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; @@ -4537,11 +4547,15 @@ public: Associativity getAssociativity() const { 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. diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 9c483180a31..3799731fa11 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -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, diff --git a/include/swift/Serialization/ModuleFormat.h b/include/swift/Serialization/ModuleFormat.h index 131e483f4f9..e61a09b9414 100644 --- a/include/swift/Serialization/ModuleFormat.h +++ b/include/swift/Serialization/ModuleFormat.h @@ -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< diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index c8cc575197b..6906662bec1 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -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 << "}"; diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 80178f2b5fd..0a19fe22f1b 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -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); } diff --git a/lib/Sema/TypeCheckExpr.cpp b/lib/Sema/TypeCheckExpr.cpp index af0919c2780..501f0d2575c 100644 --- a/lib/Sema/TypeCheckExpr.cpp +++ b/lib/Sema/TypeCheckExpr.cpp @@ -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(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(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(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) { diff --git a/lib/Serialization/Deserialization.cpp b/lib/Serialization/Deserialization.cpp index de9b388e6d0..5223d7bc554 100644 --- a/lib/Serialization/Deserialization.cpp +++ b/lib/Serialization/Deserialization.cpp @@ -2227,13 +2227,17 @@ Decl *ModuleFile::getDecl(DeclID DID, Optional 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 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 ForcedContext) { SourceLoc(), SourceLoc(), isPrecedenceImplicit, SourceLoc(), SourceLoc(), + isAssignmentImplicit, + SourceLoc(), SourceLoc(), infixData); break; } diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 03d0a524eb2..748737a4d20 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -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; } diff --git a/test/IDE/print_ast_tc_decls.swift b/test/IDE/print_ast_tc_decls.swift index d6f36129823..bb430ae0d91 100644 --- a/test/IDE/print_ast_tc_decls.swift +++ b/test/IDE/print_ast_tc_decls.swift @@ -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: {{^}}}{{$}} diff --git a/test/Parse/operator_decl.swift b/test/Parse/operator_decl.swift index d3130fbc77b..3b7d939f35c 100644 --- a/test/Parse/operator_decl.swift +++ b/test/Parse/operator_decl.swift @@ -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}} +} + diff --git a/test/Serialization/Inputs/def_operator.swift b/test/Serialization/Inputs/def_operator.swift index f61ed6f3762..0704ac3bf9a 100644 --- a/test/Serialization/Inputs/def_operator.swift +++ b/test/Serialization/Inputs/def_operator.swift @@ -14,6 +14,7 @@ infix operator -* { infix operator *-* { associativity none precedence 10 + assignment } prefix public func ~~~(x: Bool) -> () {}