Search code examples
swiftoperator-overloading

Why doesn't Swift allow custom prefix-less-than and postfix-greater-than operators?


It's singled out as specifically not allowed, but I can't find rationale. Is there any explicit, specific mention of the reason for this?


Solution

  • Swift prevents some operators from being defined because they would otherwise conflict with existing language features:

    1. Prefix & is reserved for inout arguments
    2. Postfix ! is reserved for optional unwrapping
    3. Postfix ? is reserved for optional chaining
    4. Infix ? is reserved for the ternary operator
    5. Prefix < and postfix > are reserved for defining generic argument lists
    6. = is reserved unconditionally for assignment
    7. Prefix ? and unconditional -> are reserved, but not actually used in the language

    I'm not sure if this is explicitly documented anywhere, but I tend to search through the compiler source for this sort of thing.

    If you try to compile

    // Test.swift
    prefix operator <
    

    the compiler will error:

    error: cannot declare a custom prefix '<' operator
    prefix operator <
           ^
    

    We can use the message to find where this error comes from in the Swift compiler: searching for "cannot declare a custom" in the source code brings up a result in DiagnosticsSema.def (where diagnostic messages from the semantic validation stage of the compiler are written):

    ERROR(redefining_builtin_operator,none,
          "cannot declare a custom %0 '%1' operator", (StringRef, StringRef))
    

    Searching redefining_builtin_operator then brings up two hits in AttributeChecker::checkOperatorAttribute, each following a check for isBuiltinOperator.

    isBuiltinOperator itself then gives us the above list of reserved operators:

    /// Return true if this is a builtin operator that cannot be defined in user
    /// code.
    static bool isBuiltinOperator(StringRef name, DeclAttribute *attr) {
      return ((isa<PrefixAttr>(attr)  && name == "&") ||   // lvalue to inout
              (isa<PostfixAttr>(attr) && name == "!") ||   // optional unwrapping
              // FIXME: Not actually a builtin operator, but should probably
              // be allowed and accounted for in Sema?
              (isa<PrefixAttr>(attr)  && name == "?") ||
              (isa<PostfixAttr>(attr) && name == "?") ||   // optional chaining
              (isa<InfixAttr>(attr)   && name == "?") ||   // ternary operator
              (isa<PostfixAttr>(attr) && name == ">") ||   // generic argument list
              (isa<PrefixAttr>(attr)  && name == "<") ||   // generic argument list
                                         name == "="  ||   // Assignment
              // FIXME: Should probably be allowed in expression position?
                                         name == "->");
    }
    

    From a parsing standpoint, I think prefix < and postfix > could theoretically be allowed as operators, but I think this is largely prevented to avoid possible confusion when reading source code that would otherwise look ambiguous or incorrect to a human.