The platform in question which triggered this lint being added is the windows x86 ABI, which pre-msvc version 19.14 emitted an error when passing complex aggregate types as parameters due to not being able to align them. Unfortunately modern versions of Clang still have a bug in the implementation of the post-19.14 ABI and will pass types with incorrect alignments. This change makes the intended behaviour more clear and avoids linting against alignments <= the pointer alignment on all platforms, as well as alignment for types which will be re-aligned in the callee by clang. Finally, it also adds an exception for the `std::function` stdlib type which is 16-byte aligned on macOS, but has a legal alignment on windows x86, and therefore should not be linted against as it will behave correctly on all platforms. In the future, if we update to a version of clang without this ABI bug, we may want to remove this lint entirely. Differential Revision: https://phabricator.services.mozilla.com/D132997
188 lines
9.4 KiB
C++
188 lines
9.4 KiB
C++
#define MOZ_NON_PARAM __attribute__((annotate("moz_non_param")))
|
|
|
|
struct Param {};
|
|
struct MOZ_NON_PARAM NonParam {};
|
|
union MOZ_NON_PARAM NonParamUnion {};
|
|
class MOZ_NON_PARAM NonParamClass {};
|
|
enum MOZ_NON_PARAM NonParamEnum { X, Y, Z };
|
|
enum class MOZ_NON_PARAM NonParamEnumClass { X, Y, Z };
|
|
|
|
struct HasNonParamStruct { NonParam x; int y; }; // expected-note 14 {{'HasNonParamStruct' is a non-param type because member 'x' is a non-param type 'NonParam'}}
|
|
union HasNonParamUnion { NonParam x; int y; }; // expected-note 18 {{'HasNonParamUnion' is a non-param type because member 'x' is a non-param type 'NonParam'}}
|
|
struct HasNonParamStructUnion { HasNonParamUnion z; }; // expected-note 9 {{'HasNonParamStructUnion' is a non-param type because member 'z' is a non-param type 'HasNonParamUnion'}}
|
|
|
|
#define MAYBE_STATIC
|
|
#include "NonParameterTestCases.h"
|
|
#undef MAYBE_STATIC
|
|
|
|
// Do not check typedef and using.
|
|
typedef void (*funcTypeParam)(Param x);
|
|
typedef void (*funcTypeNonParam)(NonParam x);
|
|
|
|
using usingFuncTypeParam = void (*)(Param x);
|
|
using usingFuncTypeNonParam = void (*)(NonParam x);
|
|
|
|
class class_
|
|
{
|
|
explicit class_(Param x) {}
|
|
explicit class_(NonParam x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}}
|
|
explicit class_(HasNonParamStruct x) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}}
|
|
explicit class_(HasNonParamUnion x) {} //expected-error {{Type 'HasNonParamUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}}
|
|
explicit class_(HasNonParamStructUnion x) {} //expected-error {{Type 'HasNonParamStructUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}}
|
|
|
|
#define MAYBE_STATIC
|
|
#include "NonParameterTestCases.h"
|
|
#undef MAYBE_STATIC
|
|
};
|
|
|
|
class classWithStatic
|
|
{
|
|
#define MAYBE_STATIC static
|
|
#include "NonParameterTestCases.h"
|
|
#undef MAYBE_STATIC
|
|
};
|
|
|
|
template <typename T>
|
|
class tmplClassForParam
|
|
{
|
|
public:
|
|
void raw(T x) {}
|
|
void rawDefault(T x = T()) {}
|
|
void const_(const T x) {}
|
|
void ptr(T* x) {}
|
|
void ref(T& x) {}
|
|
void constRef(const T& x) {}
|
|
|
|
void notCalled(T x) {}
|
|
};
|
|
|
|
template <typename T>
|
|
class tmplClassForNonParam
|
|
{
|
|
public:
|
|
void raw(T x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}}
|
|
void rawDefault(T x = T()) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}}
|
|
void const_(const T x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}}
|
|
void ptr(T* x) {}
|
|
void ref(T& x) {}
|
|
void constRef(const T& x) {}
|
|
|
|
void notCalled(T x) {}
|
|
};
|
|
|
|
template <typename T>
|
|
class tmplClassForHasNonParamStruct
|
|
{
|
|
public:
|
|
void raw(T x) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}}
|
|
void rawDefault(T x = T()) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}}
|
|
void const_(const T x) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}}
|
|
void ptr(T* x) {}
|
|
void ref(T& x) {}
|
|
void constRef(const T& x) {}
|
|
|
|
void notCalled(T x) {}
|
|
};
|
|
|
|
void testTemplateClass()
|
|
{
|
|
tmplClassForParam<Param> paramClass;
|
|
Param param;
|
|
paramClass.raw(param);
|
|
paramClass.rawDefault();
|
|
paramClass.const_(param);
|
|
paramClass.ptr(¶m);
|
|
paramClass.ref(param);
|
|
paramClass.constRef(param);
|
|
|
|
tmplClassForNonParam<NonParam> nonParamClass; //expected-note 3 {{The bad argument was passed to 'tmplClassForNonParam' here}}
|
|
NonParam nonParam;
|
|
nonParamClass.raw(nonParam);
|
|
nonParamClass.rawDefault();
|
|
nonParamClass.const_(nonParam);
|
|
nonParamClass.ptr(&nonParam);
|
|
nonParamClass.ref(nonParam);
|
|
nonParamClass.constRef(nonParam);
|
|
|
|
tmplClassForHasNonParamStruct<HasNonParamStruct> hasNonParamStructClass;//expected-note 3 {{The bad argument was passed to 'tmplClassForHasNonParamStruct' here}}
|
|
HasNonParamStruct hasNonParamStruct;
|
|
hasNonParamStructClass.raw(hasNonParamStruct);
|
|
hasNonParamStructClass.rawDefault();
|
|
hasNonParamStructClass.const_(hasNonParamStruct);
|
|
hasNonParamStructClass.ptr(&hasNonParamStruct);
|
|
hasNonParamStructClass.ref(hasNonParamStruct);
|
|
hasNonParamStructClass.constRef(hasNonParamStruct);
|
|
}
|
|
|
|
template <typename T>
|
|
class NestedTemplateInner
|
|
{
|
|
public:
|
|
void raw(T x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}}
|
|
};
|
|
|
|
template <typename T>
|
|
class nestedTemplateOuter
|
|
{
|
|
public:
|
|
void constRef(const T& x) {
|
|
NestedTemplateInner<T> inner; //expected-note {{The bad argument was passed to 'NestedTemplateInner' here}}
|
|
inner.raw(x);
|
|
}
|
|
};
|
|
|
|
void testNestedTemplateClass()
|
|
{
|
|
nestedTemplateOuter<NonParam> outer;
|
|
NonParam nonParam;
|
|
outer.constRef(nonParam); // FIXME: this line needs note "The bad argument was passed to 'constRef' here"
|
|
}
|
|
|
|
template <typename T>
|
|
void tmplFuncForParam(T x) {}
|
|
template <typename T>
|
|
void tmplFuncForNonParam(T x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}}
|
|
template <typename T>
|
|
void tmplFuncForNonParamImplicit(T x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}}
|
|
template <typename T>
|
|
void tmplFuncForHasNonParamStruct(T x) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}}
|
|
template <typename T>
|
|
void tmplFuncForHasNonParamStructImplicit(T x) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}}
|
|
|
|
void testTemplateFunc()
|
|
{
|
|
Param param;
|
|
tmplFuncForParam<Param>(param);
|
|
|
|
NonParam nonParam;
|
|
tmplFuncForNonParam<NonParam>(nonParam); // FIXME: this line needs note "The bad argument was passed to 'tmplFuncForNonParam' here"
|
|
tmplFuncForNonParamImplicit(nonParam); // FIXME: this line needs note "The bad argument was passed to 'tmplFuncForNonParamImplicit' here"
|
|
|
|
HasNonParamStruct hasNonParamStruct;
|
|
tmplFuncForHasNonParamStruct<HasNonParamStruct>(hasNonParamStruct); // FIXME: this line needs note "The bad argument was passed to 'tmplFuncForHasNonParamStruct' here"
|
|
tmplFuncForHasNonParamStructImplicit(hasNonParamStruct); // FIXME: this line needs note "The bad argument was passed to 'tmplFuncForHasNonParamStructImplicit' here"
|
|
}
|
|
|
|
void testLambda()
|
|
{
|
|
auto paramLambda = [](Param x) -> void {};
|
|
auto nonParamLambda = [](NonParam x) -> void {}; //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}}
|
|
auto nonParamStructLambda = [](HasNonParamStruct x) -> void {}; //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}}
|
|
auto nonParamUnionLambda = [](HasNonParamUnion x) -> void {}; //expected-error {{Type 'HasNonParamUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}}
|
|
auto nonParamStructUnionLambda = [](HasNonParamStructUnion x) -> void {}; //expected-error {{Type 'HasNonParamStructUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}}
|
|
|
|
(void)[](Param x) -> void {};
|
|
(void)[](NonParam x) -> void {}; //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}}
|
|
(void)[](HasNonParamStruct x) -> void {}; //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}}
|
|
(void)[](HasNonParamUnion x) -> void {}; //expected-error {{Type 'HasNonParamUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}}
|
|
(void)[](HasNonParamStructUnion x) -> void {}; //expected-error {{Type 'HasNonParamStructUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}}
|
|
}
|
|
|
|
struct alignas(16) AlignasStruct { char a; ~AlignasStruct(); }; // expected-note {{'AlignasStruct' is a non-param type because it has an explicit alignment of '16'}}
|
|
void takesAlignasStruct(AlignasStruct x) { } // expected-error {{Type 'AlignasStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}}
|
|
void takesAlignasStructByRef(const AlignasStruct& x) { }
|
|
|
|
struct AlignasMember { alignas(16) char a; ~AlignasMember(); }; // expected-note {{'AlignasMember' is a non-param type because member 'a' has an explicit alignment of '16'}}
|
|
void takesAlignasMember(AlignasMember x) { } // expected-error {{Type 'AlignasMember' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}}
|
|
void takesAlignasMemberByRef(const AlignasMember& x) { }
|