Skip to content

Commit 80f0852

Browse files
committed
[SE-0091] Allow 'static' operators to be declared within types and extensions thereof.
Allow 'static' (or, in classes, final 'class') operators to be declared within types and extensions thereof. Within protocols, require operators to be marked 'static'. Use a warning with a Fix-It to stage this in, so we don't break the world's code. Protocol conformance checking already seems to work, so add some tests for that. Update a pile of tests and the standard library to include the required 'static' keywords. There is an amusing name-mangling change here. Global operators were getting marked as 'static' (for silly reasons), so their mangled names had the 'Z' modifier for static methods, even though this doesn't make sense. Now, operators within types and extensions need to be 'static' as written.
1 parent d82483b commit 80f0852

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+410
-178
lines changed

include/swift/AST/Decl.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4988,7 +4988,8 @@ class FuncDecl final : public AbstractFunctionDecl,
49884988
AccessorKeywordLoc(AccessorKeywordLoc),
49894989
OverriddenOrDerivedForOrBehaviorParamDecl(),
49904990
OperatorAndAddressorKind(nullptr, AddressorKind::NotAddressor) {
4991-
FuncDeclBits.IsStatic = StaticLoc.isValid() || getName().isOperator();
4991+
FuncDeclBits.IsStatic =
4992+
StaticLoc.isValid() || StaticSpelling != StaticSpellingKind::None;
49924993
FuncDeclBits.StaticSpelling = static_cast<unsigned>(StaticSpelling);
49934994
assert(NumParameterLists > 0 && "Must have at least an empty tuple arg");
49944995
setType(Ty);

include/swift/AST/DiagnosticsParse.def

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,15 +289,16 @@ ERROR(associated_type_generic_parameter_list,PointsToFirstBadToken,
289289

290290

291291
// Func
292-
ERROR(func_decl_nonglobal_operator,none,
293-
"operators are only allowed at global scope", ())
294292
ERROR(func_decl_without_paren,PointsToFirstBadToken,
295293
"expected '(' in argument list of function declaration", ())
296294
ERROR(static_func_decl_global_scope,none,
297295
"%select{ERROR|static methods|class methods}0 may only be declared on a type",
298296
(StaticSpellingKind))
299297
ERROR(func_decl_expected_arrow,none,
300298
"expected '->' after function parameter tuple", ())
299+
WARNING(operator_static_in_protocol,none,
300+
"operator '%0' declared in protocol must be 'static'",
301+
(StringRef))
301302

302303
// Enum
303304
ERROR(expected_lbrace_enum,PointsToFirstBadToken,

include/swift/AST/DiagnosticsSema.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,13 @@ NOTE(unary_operator_declaration_here,none,
616616
"%select{prefix|postfix}0 operator found here", (bool))
617617
ERROR(invalid_arg_count_for_operator,none,
618618
"operators must have one or two arguments", ())
619+
ERROR(operator_in_local_scope,none,
620+
"operator functions can only be declared at global or in type scope", ())
621+
ERROR(nonstatic_operator_in_type,none,
622+
"operator %0 declared in type %1 must be 'static'", (Identifier, Type))
623+
ERROR(nonfinal_operator_in_class,none,
624+
"operator %0 declared in non-final class %1 must be 'final'",
625+
(Identifier, Type))
619626

620627
//------------------------------------------------------------------------------
621628
// Type Check Coercions

lib/AST/ASTPrinter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2704,7 +2704,7 @@ void PrintAST::visitFuncDecl(FuncDecl *decl) {
27042704
printSourceRange(Range, Ctx);
27052705
} else {
27062706
if (!Options.SkipIntroducerKeywords) {
2707-
if (decl->isStatic() && !decl->isOperator())
2707+
if (decl->isStatic())
27082708
printStaticKeyword(decl->getCorrectStaticSpelling());
27092709
if (decl->isMutating() && !decl->getAttrs().hasAttribute<MutatingAttr>()) {
27102710
Printer.printKeyword("mutating");

lib/AST/Decl.cpp

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4377,22 +4377,18 @@ bool FuncDecl::isUnaryOperator() const {
43774377
if (!isOperator())
43784378
return false;
43794379

4380-
unsigned opArgIndex
4381-
= getDeclContext()->getAsProtocolOrProtocolExtensionContext() ? 1 : 0;
4382-
4383-
auto *params = getParameterList(opArgIndex);
4380+
auto *params = getParameterList(getDeclContext()->isTypeContext());
43844381
return params->size() == 1 && !params->get(0)->isVariadic();
43854382
}
43864383

43874384
bool FuncDecl::isBinaryOperator() const {
43884385
if (!isOperator())
43894386
return false;
43904387

4391-
unsigned opArgIndex
4392-
= getDeclContext()->getAsProtocolOrProtocolExtensionContext() ? 1 : 0;
4393-
4394-
auto *params = getParameterList(opArgIndex);
4395-
return params->size() == 2 && !params->get(1)->isVariadic();
4388+
auto *params = getParameterList(getDeclContext()->isTypeContext());
4389+
return params->size() == 2 &&
4390+
!params->get(0)->isVariadic() &&
4391+
!params->get(1)->isVariadic();
43964392
}
43974393

43984394
ConstructorDecl::ConstructorDecl(DeclName Name, SourceLoc ConstructorLoc,

lib/Parse/ParseDecl.cpp

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4392,9 +4392,12 @@ Parser::parseDeclFunc(SourceLoc StaticLoc, StaticSpellingKind StaticSpelling,
43924392
StaticLoc = SourceLoc();
43934393
} else if (Flags.contains(PD_InStruct) || Flags.contains(PD_InEnum) ||
43944394
Flags.contains(PD_InProtocol)) {
4395-
if (StaticSpelling == StaticSpellingKind::KeywordClass)
4395+
if (StaticSpelling == StaticSpellingKind::KeywordClass) {
43964396
diagnose(Tok, diag::class_func_not_in_class)
43974397
.fixItReplace(StaticLoc, "static");
4398+
4399+
StaticSpelling = StaticSpellingKind::KeywordStatic;
4400+
}
43984401
}
43994402
}
44004403

@@ -4407,15 +4410,23 @@ Parser::parseDeclFunc(SourceLoc StaticLoc, StaticSpellingKind StaticSpelling,
44074410
Identifier SimpleName;
44084411
Token NameTok = Tok;
44094412
SourceLoc NameLoc = Tok.getLoc();
4410-
Token NonglobalTok = Tok;
4411-
bool NonglobalError = false;
44124413

4413-
if (!(Flags & PD_AllowTopLevel) &&
4414-
!(Flags & PD_InProtocol) &&
4415-
Tok.isAnyOperator()) {
4416-
// Postpone complaining about this error till we see if the
4417-
// DCC wants to move it below.
4418-
NonglobalError = true;
4414+
// Within a protocol, recover from a missing 'static'.
4415+
if (Tok.isAnyOperator() && (Flags & PD_InProtocol)) {
4416+
switch (StaticSpelling) {
4417+
case StaticSpellingKind::None:
4418+
diagnose(NameLoc, diag::operator_static_in_protocol, NameTok.getText())
4419+
.fixItInsert(FuncLoc, "static ");
4420+
StaticSpelling = StaticSpellingKind::KeywordStatic;
4421+
break;
4422+
4423+
case StaticSpellingKind::KeywordStatic:
4424+
// Okay, this is correct.
4425+
break;
4426+
4427+
case StaticSpellingKind::KeywordClass:
4428+
llvm_unreachable("should have been fixed above");
4429+
}
44194430
}
44204431

44214432
if (parseAnyIdentifier(SimpleName, diag::expected_identifier_in_decl,
@@ -4458,12 +4469,6 @@ Parser::parseDeclFunc(SourceLoc StaticLoc, StaticSpellingKind StaticSpelling,
44584469

44594470
DebuggerContextChange DCC(*this, SimpleName, DeclKind::Func);
44604471

4461-
if (NonglobalError && !DCC.movedToTopLevel()) {
4462-
// FIXME: Recovery here is awful.
4463-
diagnose(NonglobalTok, diag::func_decl_nonglobal_operator);
4464-
return nullptr;
4465-
}
4466-
44674472
// Parse the generic-params, if present.
44684473
Optional<Scope> GenericsScope;
44694474
GenericsScope.emplace(this, ScopeKind::Generics);

lib/Sema/ConstraintSystem.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -809,12 +809,16 @@ ConstraintSystem::getTypeOfReference(ValueDecl *value,
809809
openedFnType = openedType->castTo<FunctionType>();
810810
}
811811

812-
// The 'Self' type must be bound to an archetype.
813-
// FIXME: We eventually want to loosen this constraint, to allow us
814-
// to find operator functions both in classes and in protocols to which
815-
// a class conforms (if there's a default implementation).
816-
addArchetypeConstraint(openedFnType->getInput()->getRValueInstanceType(),
817-
getConstraintLocator(locator));
812+
// FIXME: We shouldn't need this for operators in protocols, either, but
813+
// constraint application fails without this at the moment.
814+
if (isa<ProtocolDecl>(func->getDeclContext())) {
815+
// The 'Self' type must be bound to an archetype.
816+
// FIXME: We eventually want to loosen this constraint, to allow us
817+
// to find operator functions both in classes and in protocols to which
818+
// a class conforms (if there's a default implementation).
819+
addArchetypeConstraint(openedFnType->getInput()->getRValueInstanceType(),
820+
getConstraintLocator(locator));
821+
}
818822

819823
// If we opened up any type variables, record the replacements.
820824
recordOpenedTypes(locator, replacements);
@@ -1015,7 +1019,7 @@ void ConstraintSystem::openGeneric(
10151019
// Determine whether this is the protocol 'Self' constraint we should
10161020
// skip.
10171021
if (skipProtocolSelfConstraint &&
1018-
protoDecl == outerDC->getAsProtocolOrProtocolExtensionContext() &&
1022+
protoDecl == outerDC &&
10191023
(protoDecl->getSelfInterfaceType()->getCanonicalType() ==
10201024
req.getFirstType()->getCanonicalType())) {
10211025
break;

lib/Sema/TypeCheckDecl.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3898,6 +3898,41 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
38983898
void bindFuncDeclToOperator(FuncDecl *FD) {
38993899
OperatorDecl *op = nullptr;
39003900
auto operatorName = FD->getFullName().getBaseName();
3901+
3902+
// Check for static/final/class when we're in a type.
3903+
auto dc = FD->getDeclContext();
3904+
if (dc->isTypeContext()) {
3905+
// Within a class, operator functions must be 'static' or 'final'.
3906+
if (auto classDecl = dc->getAsClassOrClassExtensionContext()) {
3907+
// For a class, we also need the function or class to be 'final'.
3908+
if (!classDecl->isFinal() && !FD->isFinal() &&
3909+
FD->getStaticSpelling() != StaticSpellingKind::KeywordStatic) {
3910+
if (!FD->isStatic()) {
3911+
TC.diagnose(FD->getLoc(), diag::nonstatic_operator_in_type,
3912+
operatorName,
3913+
dc->getDeclaredInterfaceType())
3914+
.fixItInsert(FD->getAttributeInsertionLoc(/*forModifier=*/true),
3915+
"static ");
3916+
} else {
3917+
TC.diagnose(FD->getLoc(), diag::nonfinal_operator_in_class,
3918+
operatorName, dc->getDeclaredInterfaceType())
3919+
.fixItInsert(FD->getAttributeInsertionLoc(/*forModifier=*/true),
3920+
"final ");
3921+
FD->getAttrs().add(new (TC.Context) FinalAttr(/*IsImplicit=*/true));
3922+
}
3923+
}
3924+
} else if (!FD->isStatic()) {
3925+
// Operator functions must be static.
3926+
TC.diagnose(FD, diag::nonstatic_operator_in_type,
3927+
operatorName,
3928+
dc->getDeclaredInterfaceType())
3929+
.fixItInsert(FD->getAttributeInsertionLoc(/*forModifier=*/true),
3930+
"static ");
3931+
}
3932+
} else if (!dc->isModuleScopeContext()) {
3933+
TC.diagnose(FD, diag::operator_in_local_scope);
3934+
}
3935+
39013936
SourceFile &SF = *FD->getDeclContext()->getParentSourceFile();
39023937
if (FD->isUnaryOperator()) {
39033938
if (FD->getAttrs().hasAttribute<PrefixAttr>()) {

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -707,7 +707,9 @@ matchWitness(TypeChecker &tc,
707707
auto funcWitness = cast<FuncDecl>(witness);
708708

709709
// Either both must be 'static' or neither.
710-
if (funcReq->isStatic() != funcWitness->isStatic())
710+
if (funcReq->isStatic() != funcWitness->isStatic() &&
711+
!(funcReq->isOperator() &&
712+
!funcWitness->getDeclContext()->isTypeContext()))
711713
return RequirementMatch(witness, MatchKind::StaticNonStaticConflict);
712714

713715
// If we require a prefix operator and the witness is not a prefix operator,

stdlib/public/core/ArrayType.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ protocol _ArrayProtocol
4545
mutating func reserveCapacity(_ minimumCapacity: Int)
4646

4747
/// Operator form of `append(contentsOf:)`.
48-
func += <S : Sequence>(lhs: inout Self, rhs: S)
48+
static func += <S : Sequence>(lhs: inout Self, rhs: S)
4949
where S.Iterator.Element == Iterator.Element
5050

5151
/// Insert `newElement` at index `i`.

0 commit comments

Comments
 (0)