rsandifo-arm created this revision.
rsandifo-arm added reviewers: sdesmalen, efriedma, rovka, rjmccall.
Herald added subscribers: cfe-commits, danielkiss, psnobl, rkruppe, 
kristof.beyls, tschuett.
Herald added a reviewer: rengolin.
Herald added a project: clang.
rsandifo-arm edited the summary of this revision.

SVE types are defined to be opaque built-in types that by default
can only be used via intrinsics.  One consequence of this is that
the types provide no built-in versions of the unary and binary
arithmetic operators.

Instead, we'd like to allow users to define non-member operators
for SVE types, in much the same way as for enumeration types.
This specifically means:

- replacing "enumeration" in sections [over.match.oper] and [over.oper] of the 
C++ standard with wording that includes both enumerations and SVE types.

- extending the enumeration handling of operator= in [over.built] to include 
SVE types.

This feature is defined by a to-be-published update to the SVE ACLE spec.
The feature is optional and its availability can be tested by the
preprocessor condition:

  __ARM_FEATURE_SVE_NONMEMBER_OPERATORS==1

An alternative would be to build the operators into the compiler.
However, we didn't want to do that for several reasons:

- Some of the operators would not be performance-portable. (E.g. %, and the 
8-bit and 16-bit integer versions of /.) The SVE ACLE is supposed to be a 
low-level, almost asm-level interface to the architecture, so synthesising this 
kind of operation seems out-of-place.

- SVE types cannot be used in structures, so unlike with normal vector types, 
the user doesn't have the option of using a wrapper class to opt out of the 
standard handling.

- If in future we do want to provide a core group of operators in a standard 
header file/module, we could use this extension to write it.

The patch triggers a duplicate warning for:

  ref_int8 = ref_int8;

but it seemed better to fix that separately.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D77056

Files:
  clang/include/clang/AST/Type.h
  clang/lib/Basic/Targets/AArch64.cpp
  clang/lib/Sema/SemaDeclCXX.cpp
  clang/lib/Sema/SemaOverload.cpp
  clang/test/SemaCXX/sizeless-1.cpp

Index: clang/test/SemaCXX/sizeless-1.cpp
===================================================================
--- clang/test/SemaCXX/sizeless-1.cpp
+++ clang/test/SemaCXX/sizeless-1.cpp
@@ -3,6 +3,10 @@
 // RUN: %clang_cc1 -fcxx-exceptions -fsyntax-only -verify -W -Wall -Wrange-loop-analysis -triple arm64-linux-gnu -target-feature +sve -std=c++17 %s
 // RUN: %clang_cc1 -fcxx-exceptions -fsyntax-only -verify -W -Wall -Wrange-loop-analysis -triple arm64-linux-gnu -target-feature +sve -std=gnu++17 %s
 
+#if __ARM_FEATURE_SVE_NONMEMBER_OPERATORS != 1
+#error "__ARM_FEATURE_SVE_NONMEMBER_OPERATORS should be defined"
+#endif
+
 namespace std {
 struct type_info;
 }
@@ -450,7 +454,7 @@
   local_int16 = wrapper<svint8_t>(); // expected-error {{assigning to 'svint16_t' (aka '__SVInt16_t') from incompatible type 'wrapper<svint8_t>'}}
 
   svint8_t &ref_int8 = local_int8;
-  ref_int8 = ref_int8; // expected-warning {{explicitly assigning value of variable of type 'svint8_t' (aka '__SVInt8_t') to itself}}
+  ref_int8 = ref_int8; // expected-warning + {{explicitly assigning value of variable of type 'svint8_t' (aka '__SVInt8_t') to itself}}
   ref_int8 = local_int8;
   local_int8 = ref_int8;
 
@@ -602,3 +606,160 @@
 #if __cplusplus >= 201103L
 svint8_t ret_bad_conv() { return explicit_conv(); } // expected-error {{no viable conversion from returned value of type 'explicit_conv' to function return type 'svint8_t'}}
 #endif
+
+svint8_t operator()(svint8_t);                // expected-error {{must be a non-static member function}}
+svint8_t operator()(svint8_t, int, int, int); // expected-error {{must be a non-static member function}}
+svint8_t operator[](svint8_t, int);           // expected-error {{must be a non-static member function}}
+svint8_t operator->(svint8_t);                // expected-error {{must be a non-static member function}}
+svint8_t operator~(svint8_t);
+svint8_t operator!(svint8_t);
+svint8_t operator+(svint8_t); // expected-note + {{not viable}}
+svint8_t operator-(svint8_t); // expected-note + {{not viable}}
+svint8_t operator&(svint8_t &);
+svint8_t operator+(svint8_t, svint8_t);  // expected-note + {{not viable}}
+svint8_t operator-(svint8_t, int);       // expected-note + {{not viable}}
+svint8_t operator-(int, svint8_t);       // expected-note + {{not viable}}
+svint8_t operator*(svint8_t, int);       // expected-note + {{not viable}}
+svint8_t operator*(svint8_t, svint16_t); // expected-note + {{not viable}}
+svint8_t operator/(svint8_t, svint8_t);
+svint8_t operator/(svint8_t, svint16_t); // expected-note + {{not viable}}
+svint8_t operator/(svint16_t, svint8_t); // expected-note + {{not viable}}
+svint8_t operator%(svint8_t, svint8_t);
+svint8_t operator%(svint8_t, svint16_t);
+svint8_t operator%(svint16_t, svint8_t);
+svint8_t operator%(svint16_t, svint16_t);
+svint8_t operator^(svint8_t, svint8_t);
+svint8_t operator&(svint8_t, svint8_t);
+svint8_t operator|(svint8_t, svint8_t);
+svint8_t &operator=(svint8_t &, svint8_t); // expected-error {{must be a non-static member function}}
+svint8_t &operator+=(svint8_t &, int);     // expected-note + {{not viable}}
+svint8_t &operator-=(svint8_t &, int);
+svint8_t &operator*=(svint8_t &, svint16_t);
+svint8_t &operator/=(svint8_t &, svint8_t);
+svint8_t &operator%=(svint8_t &, svint8_t);
+svint8_t &operator^=(svint8_t &, svint8_t);
+svint8_t &operator&=(svint8_t &, svint8_t);
+svint8_t &operator|=(svint8_t &, svint8_t);
+bool operator==(svint8_t, svint8_t);  // expected-note + {{not viable}}
+bool operator!=(svint8_t, svint16_t); // expected-note + {{not viable}}
+bool operator<(svint8_t, svint8_t);
+bool operator<(svint8_t, svint16_t);
+bool operator>(svint8_t, svint8_t);
+bool operator>(svint8_t, svint16_t);
+bool operator<=(svint8_t, svint8_t);
+bool operator>=(svint8_t, svint8_t);
+svint8_t operator<<(svint8_t, svint8_t);
+svint8_t operator<<(int, svint8_t);
+svint8_t operator>>(svint8_t, svint8_t);
+svint8_t operator>>(int, svint8_t);
+svint8_t &operator<<=(svint8_t &, svint8_t); // expected-note + {{not viable}}
+svint8_t &operator>>=(svint8_t &, int);      // expected-note + {{not viable}}
+svint8_t &operator++(svint8_t &);
+svint8_t &operator--(svint8_t &, int);
+int operator,(svint8_t, svint8_t);
+
+void callers(svint8_t x, svint16_t y, svint8_t z) {
+  x();        // expected-error {{not a function or function pointer}}
+  x(1, 2);    // expected-error {{not a function or function pointer}}
+  x(1, 2, 3); // expected-error {{not a function or function pointer}}
+
+  x[1]; // expected-error {{not an array, pointer, or vector}}
+
+  x = ~x;
+  y = ~y; // expected-error {{invalid argument type}}
+
+  x = !x;
+  y = !y; // expected-error {{invalid argument type}}
+
+  x = +x;
+  y = +y; // expected-error {{invalid argument type}}
+
+  x = -x;
+  y = -y; // expected-error {{invalid argument type}}
+
+  x = &x;
+  svint16_t *ptr = &y;
+
+  x = x + x;
+  x = x + 1; // expected-error {{invalid operands to binary expression}}
+  x = 1 + x; // expected-error {{invalid operands to binary expression}}
+  x = y + y; // expected-error {{invalid operands to binary expression}}
+  x = y + x; // expected-error {{invalid operands to binary expression}}
+  x = x + y; // expected-error {{invalid operands to binary expression}}
+
+  x = x - x; // expected-error {{invalid operands to binary expression}}
+  x = x - 1;
+  x = 1 - x;
+  x = y - y; // expected-error {{invalid operands to binary expression}}
+  x = y - x; // expected-error {{invalid operands to binary expression}}
+  x = x - y; // expected-error {{invalid operands to binary expression}}
+
+  x = x * x; // expected-error {{invalid operands to binary expression}}
+  x = x * 1;
+  x = 1 * x; // expected-error {{invalid operands to binary expression}}
+  x = y * y; // expected-error {{invalid operands to binary expression}}
+  x = y * x; // expected-error {{invalid operands to binary expression}}
+  x = x * y;
+
+  x = x / x;
+  x = x / y;
+  x = y / x;
+  x = y / y; // expected-error {{invalid operands to binary expression}}
+
+  x = x % x;
+  x = x % y;
+  x = y % x;
+  x = y % y;
+
+  x += x; // expected-error {{invalid operands to binary expression}}
+  x += 1;
+  x += y; // expected-error {{invalid operands to binary expression}}
+
+  x -= 1;
+  x *= y;
+  x /= z;
+  x %= z;
+  x ^= z;
+  x &= z;
+  x |= z;
+
+  bool cond;
+  cond = (x == y); // expected-error {{invalid operands to binary expression}}
+  cond = (x == z);
+
+  cond = (x != y);
+  cond = (x != z); // expected-error {{invalid operands to binary expression}}
+
+  cond = (x < y);
+  cond = (x < z);
+
+  cond = (x > y);
+  cond = (x > z);
+
+  cond = (x <= x);
+  cond = (x >= z);
+
+  x = x << z;
+  x = 1 << x;
+
+  x = x >> z;
+  x = 1 >> x;
+
+  x <<= z;
+  x <<= 1; // expected-error {{invalid operands to binary expression}}
+
+  x >>= z; // expected-error {{invalid operands to binary expression}}
+  x >>= 1;
+
+  ++x;
+  x++; // expected-error {{cannot increment value}}
+
+  --x; // expected-error {{cannot decrement value}}
+  x--;
+
+  int res1 = (x, z);
+  svint8_t res2 = (x, z); // expected-error {{cannot initialize}}
+
+  int res3 = (x, y);       // expected-error {{cannot initialize}} expected-warning {{result unused}}
+  svint16_t res4 = (x, y); // expected-warning {{result unused}}
+}
Index: clang/lib/Sema/SemaOverload.cpp
===================================================================
--- clang/lib/Sema/SemaOverload.cpp
+++ clang/lib/Sema/SemaOverload.cpp
@@ -6118,7 +6118,7 @@
   if (Proto->getNumParams() < 1)
     return false;
 
-  if (T1->isEnumeralType()) {
+  if (T1->isEnumeralType() || T1->isSizelessBuiltinType()) {
     QualType ArgType = Proto->getParamType(0).getNonReferenceType();
     if (Context.hasSameUnqualifiedType(T1, ArgType))
       return true;
@@ -6127,7 +6127,7 @@
   if (Proto->getNumParams() < 2)
     return false;
 
-  if (!T2.isNull() && T2->isEnumeralType()) {
+  if (!T2.isNull() && (T2->isEnumeralType() || T2->isSizelessBuiltinType())) {
     QualType ArgType = Proto->getParamType(1).getNonReferenceType();
     if (Context.hasSameUnqualifiedType(T2, ArgType))
       return true;
@@ -7687,6 +7687,10 @@
   /// candidates.
   TypeSet VectorTypes;
 
+  /// The set of sizeless builtin types that will be used in the
+  /// built-in candidates, notably for the assignment operator.
+  TypeSet SizelessBuiltinTypes;
+
   /// A flag indicating non-record types are viable candidates
   bool HasNonRecordTypes;
 
@@ -7747,6 +7751,9 @@
   iterator vector_begin() { return VectorTypes.begin(); }
   iterator vector_end() { return VectorTypes.end(); }
 
+  iterator sizeless_builtin_begin() { return SizelessBuiltinTypes.begin(); }
+  iterator sizeless_builtin_end() { return SizelessBuiltinTypes.end(); }
+
   bool hasNonRecordTypes() { return HasNonRecordTypes; }
   bool hasArithmeticOrEnumeralTypes() { return HasArithmeticOrEnumeralTypes; }
   bool hasNullPtrType() const { return HasNullPtrType; }
@@ -7921,6 +7928,8 @@
     // extension.
     HasArithmeticOrEnumeralTypes = true;
     VectorTypes.insert(Ty);
+  } else if (Ty->isSizelessBuiltinType()) {
+    SizelessBuiltinTypes.insert(Ty);
   } else if (Ty->isNullPtrType()) {
     HasNullPtrType = true;
   } else if (AllowUserConversions && TyRec) {
@@ -8643,25 +8652,27 @@
     llvm::SmallPtrSet<QualType, 8> AddedTypes;
 
     for (unsigned ArgIdx = 0; ArgIdx < 2; ++ArgIdx) {
-      for (BuiltinCandidateTypeSet::iterator
-                Enum = CandidateTypes[ArgIdx].enumeration_begin(),
-             EnumEnd = CandidateTypes[ArgIdx].enumeration_end();
-           Enum != EnumEnd; ++Enum) {
-        if (!AddedTypes.insert(S.Context.getCanonicalType(*Enum)).second)
-          continue;
+      auto AddAssignmentCandidates =
+          [&](BuiltinCandidateTypeSet::iterator Begin,
+              BuiltinCandidateTypeSet::iterator End) {
+            for (BuiltinCandidateTypeSet::iterator Type = Begin; Type != End;
+                 ++Type) {
+              if (!AddedTypes.insert(S.Context.getCanonicalType(*Type)).second)
+                continue;
+
+              AddBuiltinAssignmentOperatorCandidates(S, *Type, Args,
+                                                     CandidateSet);
+            }
+          };
 
-        AddBuiltinAssignmentOperatorCandidates(S, *Enum, Args, CandidateSet);
-      }
+      AddAssignmentCandidates(CandidateTypes[ArgIdx].enumeration_begin(),
+                              CandidateTypes[ArgIdx].enumeration_end());
 
-      for (BuiltinCandidateTypeSet::iterator
-                MemPtr = CandidateTypes[ArgIdx].member_pointer_begin(),
-             MemPtrEnd = CandidateTypes[ArgIdx].member_pointer_end();
-           MemPtr != MemPtrEnd; ++MemPtr) {
-        if (!AddedTypes.insert(S.Context.getCanonicalType(*MemPtr)).second)
-          continue;
+      AddAssignmentCandidates(CandidateTypes[ArgIdx].member_pointer_begin(),
+                              CandidateTypes[ArgIdx].member_pointer_end());
 
-        AddBuiltinAssignmentOperatorCandidates(S, *MemPtr, Args, CandidateSet);
-      }
+      AddAssignmentCandidates(CandidateTypes[ArgIdx].sizeless_builtin_begin(),
+                              CandidateTypes[ArgIdx].sizeless_builtin_end());
     }
   }
 
Index: clang/lib/Sema/SemaDeclCXX.cpp
===================================================================
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -15259,17 +15259,16 @@
       return Diag(FnDecl->getLocation(),
                   diag::err_operator_overload_static) << FnDecl->getDeclName();
   } else {
-    bool ClassOrEnumParam = false;
+    bool OverloadableParam = false;
     for (auto Param : FnDecl->parameters()) {
       QualType ParamType = Param->getType().getNonReferenceType();
-      if (ParamType->isDependentType() || ParamType->isRecordType() ||
-          ParamType->isEnumeralType()) {
-        ClassOrEnumParam = true;
+      if (ParamType->isOverloadableType()) {
+        OverloadableParam = true;
         break;
       }
     }
 
-    if (!ClassOrEnumParam)
+    if (!OverloadableParam)
       return Diag(FnDecl->getLocation(),
                   diag::err_operator_overload_needs_class_or_enum)
         << FnDecl->getDeclName();
Index: clang/lib/Basic/Targets/AArch64.cpp
===================================================================
--- clang/lib/Basic/Targets/AArch64.cpp
+++ clang/lib/Basic/Targets/AArch64.cpp
@@ -282,6 +282,9 @@
   if ((FPU & NeonMode) && HasFP16FML)
     Builder.defineMacro("__ARM_FEATURE_FP16FML", "1");
 
+  if (FPU & SveMode)
+    Builder.defineMacro("__ARM_FEATURE_SVE_NONMEMBER_OPERATORS", "1");
+
   switch (ArchKind) {
   default:
     break;
Index: clang/include/clang/AST/Type.h
===================================================================
--- clang/include/clang/AST/Type.h
+++ clang/include/clang/AST/Type.h
@@ -6830,7 +6830,8 @@
 /// Determines whether this is a type for which one can define
 /// an overloaded operator.
 inline bool Type::isOverloadableType() const {
-  return isDependentType() || isRecordType() || isEnumeralType();
+  return (isDependentType() || isRecordType() || isEnumeralType() ||
+          isSizelessBuiltinType());
 }
 
 /// Determines whether this type can decay to a pointer type.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to