diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -7046,7 +7046,11 @@ /// Determines whether this is a type for which one can define /// an overloaded operator. inline bool Type::isOverloadableType() const { - return isDependentType() || isRecordType() || isEnumeralType(); + // Sizeless SVE types are opaque types that explicitly support + // operator overloads. For now we assume that any other sizeless + // built-in types would inherit the same behavior. + return isDependentType() || isRecordType() || isEnumeralType() || + isSizelessBuiltinType(); } /// Determines whether this type can decay to a pointer type. diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -8662,6 +8662,9 @@ def err_operator_overload_must_be : Error< "overloaded %0 must be a %select{unary|binary|unary or binary}2 operator " "(has %1 parameter%s1)">; +def err_operator_overload_must_be_static_or_in_namespace : Error< + "overloads of %0 for sizeless types must either be declared 'static' or " + "be declared inside a namespace">; def err_operator_overload_must_be_member : Error< "overloaded %0 must be a non-static member function">; diff --git a/clang/lib/Basic/Targets/AArch64.cpp b/clang/lib/Basic/Targets/AArch64.cpp --- a/clang/lib/Basic/Targets/AArch64.cpp +++ b/clang/lib/Basic/Targets/AArch64.cpp @@ -348,6 +348,9 @@ if (Opts.BranchTargetEnforcement) Builder.defineMacro("__ARM_FEATURE_BTI_DEFAULT", "1"); + if (FPU & SveMode) + Builder.defineMacro("__ARM_FEATURE_SVE_NONMEMBER_OPERATORS", "1"); + switch (ArchKind) { default: break; diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -15418,22 +15418,24 @@ // function or be a non-member function and have at least one // parameter whose type is a class, a reference to a class, an // enumeration, or a reference to an enumeration. + bool NeedStaticOrNamespace = false; if (CXXMethodDecl *MethodDecl = dyn_cast(FnDecl)) { if (MethodDecl->isStatic()) return Diag(FnDecl->getLocation(), diag::err_operator_overload_static) << FnDecl->getDeclName(); } else { - bool ClassOrEnumParam = false; + NeedStaticOrNamespace = true; + bool OverloadableParam = false; for (auto Param : FnDecl->parameters()) { QualType ParamType = Param->getType().getNonReferenceType(); - if (ParamType->isDependentType() || ParamType->isRecordType() || - ParamType->isEnumeralType()) { - ClassOrEnumParam = true; - break; + if (ParamType->isOverloadableType()) { + OverloadableParam = true; + if (!ParamType->isSizelessBuiltinType()) + NeedStaticOrNamespace = false; } } - if (!ClassOrEnumParam) + if (!OverloadableParam) return Diag(FnDecl->getLocation(), diag::err_operator_overload_needs_class_or_enum) << FnDecl->getDeclName(); @@ -15526,6 +15528,12 @@ << LastParam->getType() << (Op == OO_MinusMinus); } + if (NeedStaticOrNamespace && FnDecl->getStorageClass() != SC_Static && + !FnDecl->getDeclContext()->isNamespace()) + return Diag(FnDecl->getLocation(), + diag::err_operator_overload_must_be_static_or_in_namespace) + << FnDecl->getDeclName(); + return false; } diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -6234,7 +6234,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; @@ -6243,7 +6243,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; @@ -7804,6 +7804,10 @@ /// candidates. TypeSet MatrixTypes; + /// 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; @@ -7852,8 +7856,12 @@ } llvm::iterator_range vector_types() { return VectorTypes; } llvm::iterator_range matrix_types() { return MatrixTypes; } + llvm::iterator_range sizeless_builtin_types() { + return SizelessBuiltinTypes; + } bool containsMatrixType(QualType Ty) const { return MatrixTypes.count(Ty); } + bool hasNonRecordTypes() { return HasNonRecordTypes; } bool hasArithmeticOrEnumeralTypes() { return HasArithmeticOrEnumeralTypes; } bool hasNullPtrType() const { return HasNullPtrType; } @@ -8033,6 +8041,8 @@ // many contexts as an extension. HasArithmeticOrEnumeralTypes = true; MatrixTypes.insert(Ty); + } else if (Ty->isSizelessBuiltinType()) { + SizelessBuiltinTypes.insert(Ty); } else if (Ty->isNullPtrType()) { HasNullPtrType = true; } else if (AllowUserConversions && TyRec) { @@ -8740,21 +8750,16 @@ /// Set of (canonical) types that we've already handled. llvm::SmallPtrSet AddedTypes; - for (unsigned ArgIdx = 0; ArgIdx < 2; ++ArgIdx) { - for (QualType EnumTy : CandidateTypes[ArgIdx].enumeration_types()) { - if (!AddedTypes.insert(S.Context.getCanonicalType(EnumTy)).second) - continue; - - AddBuiltinAssignmentOperatorCandidates(S, EnumTy, Args, CandidateSet); - } - - for (QualType MemPtrTy : CandidateTypes[ArgIdx].member_pointer_types()) { - if (!AddedTypes.insert(S.Context.getCanonicalType(MemPtrTy)).second) - continue; + for (unsigned ArgIdx = 0; ArgIdx < 2; ++ArgIdx) + for (auto Types : {CandidateTypes[ArgIdx].enumeration_types(), + CandidateTypes[ArgIdx].member_pointer_types(), + CandidateTypes[ArgIdx].sizeless_builtin_types()}) + for (QualType Type : Types) { + if (!AddedTypes.insert(S.Context.getCanonicalType(Type)).second) + continue; - AddBuiltinAssignmentOperatorCandidates(S, MemPtrTy, Args, CandidateSet); - } - } + AddBuiltinAssignmentOperatorCandidates(S, Type, Args, CandidateSet); + } } // C++ [over.built]p19: diff --git a/clang/test/SemaCXX/sizeless-1.cpp b/clang/test/SemaCXX/sizeless-1.cpp --- a/clang/test/SemaCXX/sizeless-1.cpp +++ b/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; } @@ -461,7 +465,7 @@ local_int16 = wrapper(); // expected-error {{assigning to 'svint16_t' (aka '__SVInt16_t') from incompatible type 'wrapper'}} 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; @@ -626,3 +630,247 @@ void incompat_init() { __attribute__((unused)) svint8_t foo = {}; } // expected-warning {{initializing 'svint8_t' (aka '__SVInt8_t') from an empty initializer list is incompatible with C++98}} #endif + +struct struct1 {}; +struct struct2 {}; + +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}} +static svint8_t operator~(svint8_t); +namespace { +svint8_t operator!(svint8_t); +} +namespace operators { +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); +} // namespace operators + +svint8_t operator-(svint8_t); // expected-error {{overloads of 'operator-' for sizeless types must either be declared 'static' or be declared inside a namespace}} + +int operator<<(svint8_t, struct1); +int operator<<(struct1, svint8_t); +int operator<<(svint8_t, svint8_t); // expected-error {{overloads of 'operator<<' for sizeless types must either be declared 'static' or be declared inside a namespace}} +int operator<<(svint8_t, int); // expected-error {{overloads of 'operator<<' for sizeless types must either be declared 'static' or be declared inside a namespace}} +int operator<<(int, svint8_t); // expected-error {{overloads of 'operator<<' for sizeless types must either be declared 'static' or be declared inside a namespace}} + +template +struct traits { +}; + +template <> +struct traits { + typedef svint8_t type; +}; + +template <> +struct traits { + typedef svint16_t type; +}; + +namespace template_operators { +template +struct1 operator+(struct1, T); // expected-note + {{not viable}} + +template +struct1 operator-(T, struct1); // expected-note + {{not viable}} + +template +T operator*(T, typename traits::type); // expected-note + {{not viable}} + +template +T1 operator/(T1, T2); +} // namespace template_operators + +void callers(svint8_t x, svint16_t y, svint8_t z, struct1 s1, struct2 s2) { + 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; // expected-error {{invalid argument type}} + + using namespace operators; + + 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}} + + using namespace template_operators; + + s1 = s1 + x; + s1 = s1 + y; + + s2 = s2 + x; // expected-error {{invalid operands to binary expression}} + s2 = s2 + y; // expected-error {{invalid operands to binary expression}} + + s1 = x - s1; + s1 = y - s1; + + s2 = x - s2; // expected-error {{invalid operands to binary expression}} + s2 = y - s2; // expected-error {{invalid operands to binary expression}} + + s1 = s1 * x; + s1 = s1 * y; // expected-error {{invalid operands to binary expression}} + + s2 = s2 * x; // expected-error {{invalid operands to binary expression}} + s2 = s2 * y; + + s1 = s1 / x; + s1 = s1 / y; + + s2 = s2 / x; + s2 = s2 / y; + + x = x / y; +} + +template +T1 operator*(T1, T2); + +template <> +svint8_t operator*(svint8_t, struct1); + +template +T1 operator/(T1, T2); // expected-error {{verloads of 'operator/' for sizeless types must either be declared 'static' or be declared inside a namespace}} expected-note {{candidate template ignored}} + +template <> +svint8_t operator/(svint8_t, svint16_t); // expected-error {{no function template matches function template specialization 'operator/'}} expected-note {{in instantiation}}