Skip to content

Suppress errors from well-formed-testing type traits in SFINAE contexts #135390

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,19 @@ Bug Fixes to C++ Support
- Clang is now better at keeping track of friend function template instance contexts. (#GH55509)
- Clang now prints the correct instantiation context for diagnostics suppressed
by template argument deduction.
- Errors that occur during evaluation of certain type traits and builtins are
no longer incorrectly emitted when they are used in an SFINAE context. The
type traits are:

- ``__is_constructible`` and variants,
- ``__is_convertible`` and variants,
- ``__is_assignable`` and variants,
- ``__reference_binds_to_temporary``,
``__reference_constructs_from_temporary``,
``__reference_converts_from_temporary``,
- ``__is_trivially_equality_comparable``.

The builtin is ``__builtin_common_type``. (#GH132044)
- Clang is now better at instantiating the function definition after its use inside
of a constexpr lambda. (#GH125747)
- Fixed a local class member function instantiation bug inside dependent lambdas. (#GH59734), (#GH132208)
Expand Down
11 changes: 7 additions & 4 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -12365,16 +12365,19 @@ class Sema final : public SemaBase {
bool PrevLastDiagnosticIgnored;

public:
explicit SFINAETrap(Sema &SemaRef, bool AccessCheckingSFINAE = false)
/// \param ForValidityCheck If true, discard all diagnostics (from the
/// immediate context) instead of adding them to the currently active
/// \ref TemplateDeductionInfo (as returned by \ref isSFINAEContext).
explicit SFINAETrap(Sema &SemaRef, bool ForValidityCheck = false)
: SemaRef(SemaRef), PrevSFINAEErrors(SemaRef.NumSFINAEErrors),
PrevInNonInstantiationSFINAEContext(
SemaRef.InNonInstantiationSFINAEContext),
PrevAccessCheckingSFINAE(SemaRef.AccessCheckingSFINAE),
PrevLastDiagnosticIgnored(
SemaRef.getDiagnostics().isLastDiagnosticIgnored()) {
if (!SemaRef.isSFINAEContext())
if (ForValidityCheck || !SemaRef.isSFINAEContext())
SemaRef.InNonInstantiationSFINAEContext = true;
SemaRef.AccessCheckingSFINAE = AccessCheckingSFINAE;
SemaRef.AccessCheckingSFINAE = ForValidityCheck;
}

~SFINAETrap() {
Expand Down Expand Up @@ -12404,7 +12407,7 @@ class Sema final : public SemaBase {

public:
explicit TentativeAnalysisScope(Sema &SemaRef)
: SemaRef(SemaRef), Trap(SemaRef, true),
: SemaRef(SemaRef), Trap(SemaRef, /*ForValidityCheck=*/true),
PrevDisableTypoCorrection(SemaRef.DisableTypoCorrection) {
SemaRef.DisableTypoCorrection = true;
}
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaConcept.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -907,7 +907,7 @@ static const Expr *SubstituteConstraintExpressionWithoutSatisfaction(
if (MLTAL.getNumSubstitutedLevels() == 0)
return ConstrExpr;

Sema::SFINAETrap SFINAE(S, /*AccessCheckingSFINAE=*/false);
Sema::SFINAETrap SFINAE(S);

Sema::InstantiatingTemplate Inst(
S, DeclInfo.getLocation(),
Expand Down
8 changes: 4 additions & 4 deletions clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5523,7 +5523,7 @@ static bool HasNonDeletedDefaultedEqualityComparison(Sema &S,
{
EnterExpressionEvaluationContext UnevaluatedContext(
S, Sema::ExpressionEvaluationContext::Unevaluated);
Sema::SFINAETrap SFINAE(S, /*AccessCheckingSFINAE=*/true);
Sema::SFINAETrap SFINAE(S, /*ForValidityCheck=*/true);
Sema::ContextRAII TUContext(S, S.Context.getTranslationUnitDecl());

// const ClassT& obj;
Expand Down Expand Up @@ -6232,7 +6232,7 @@ static ExprResult CheckConvertibilityForTypeTraits(
// trap at translation unit scope.
EnterExpressionEvaluationContext Unevaluated(
Self, Sema::ExpressionEvaluationContext::Unevaluated);
Sema::SFINAETrap SFINAE(Self, /*AccessCheckingSFINAE=*/true);
Sema::SFINAETrap SFINAE(Self, /*ForValidityCheck=*/true);
Sema::ContextRAII TUContext(Self, Self.Context.getTranslationUnitDecl());
InitializationSequence Init(Self, To, Kind, From);
if (Init.Failed())
Expand Down Expand Up @@ -6354,7 +6354,7 @@ static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind,
// trap at translation unit scope.
EnterExpressionEvaluationContext Unevaluated(
S, Sema::ExpressionEvaluationContext::Unevaluated);
Sema::SFINAETrap SFINAE(S, /*AccessCheckingSFINAE=*/true);
Sema::SFINAETrap SFINAE(S, /*ForValidityCheck=*/true);
Sema::ContextRAII TUContext(S, S.Context.getTranslationUnitDecl());
InitializedEntity To(
InitializedEntity::InitializeTemporary(S.Context, Args[0]));
Expand Down Expand Up @@ -6697,7 +6697,7 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceI
// trap at translation unit scope.
EnterExpressionEvaluationContext Unevaluated(
Self, Sema::ExpressionEvaluationContext::Unevaluated);
Sema::SFINAETrap SFINAE(Self, /*AccessCheckingSFINAE=*/true);
Sema::SFINAETrap SFINAE(Self, /*ForValidityCheck=*/true);
Sema::ContextRAII TUContext(Self, Self.Context.getTranslationUnitDecl());
ExprResult Result = Self.BuildBinOp(/*S=*/nullptr, KeyLoc, BO_Assign, &Lhs,
&Rhs);
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3118,7 +3118,7 @@ static QualType builtinCommonTypeImpl(Sema &S, TemplateName BaseTemplate,

EnterExpressionEvaluationContext UnevaluatedContext(
S, Sema::ExpressionEvaluationContext::Unevaluated);
Sema::SFINAETrap SFINAE(S, /*AccessCheckingSFINAE=*/true);
Sema::SFINAETrap SFINAE(S, /*ForValidityCheck=*/true);
Sema::ContextRAII TUContext(S, S.Context.getTranslationUnitDecl());

QualType BaseTemplateInst =
Expand Down Expand Up @@ -3164,7 +3164,7 @@ static QualType builtinCommonTypeImpl(Sema &S, TemplateName BaseTemplate,
auto CheckConditionalOperands = [&](bool ConstRefQual) -> QualType {
EnterExpressionEvaluationContext UnevaluatedContext(
S, Sema::ExpressionEvaluationContext::Unevaluated);
Sema::SFINAETrap SFINAE(S, /*AccessCheckingSFINAE=*/true);
Sema::SFINAETrap SFINAE(S, /*ForValidityCheck=*/true);
Sema::ContextRAII TUContext(S, S.Context.getTranslationUnitDecl());

// false
Expand Down
34 changes: 34 additions & 0 deletions clang/test/SemaCXX/type-trait-common-type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ void test_vla() {

template <class... Args>
using common_type_base = __builtin_common_type<common_type_t, type_identity, empty_type, Args...>;
// expected-note@-1 {{in instantiation of default function argument expression for 'InvalidConversion<void>' required here}}

template <class... Args>
struct common_type : common_type_base<Args...> {};
Expand Down Expand Up @@ -208,3 +209,36 @@ struct common_type<PrivateTypeMember, PrivateTypeMember>
};

static_assert(__is_same(common_type_base<PrivateTypeMember, PrivateTypeMember, PrivateTypeMember>, empty_type));

class PrivateConstructor {
private:
PrivateConstructor(int);
};

static_assert(__is_same(common_type_base<int, PrivateConstructor>, empty_type));

// expected-note@+1 {{in instantiation of template type alias 'common_type_base' requested here}}
template<typename A, typename B, typename Res = common_type_base<A, B>>
static Res common_type_sfinae();
// expected-note@-1 {{in instantiation of default argument for 'common_type_sfinae<int, InvalidConversion>' required here}}

// Make sure we don't emit "calling a private constructor" in SFINAE context ...
static_assert(__is_same(decltype(common_type_sfinae<int, PrivateConstructor>()), empty_type));

// ... but we still emit errors outside of the immediate context.
template<typename T>
struct Member {
T t; // expected-error {{field has incomplete type 'void'}}
};

// The conversion from int has a non-SFINAE error.
class InvalidConversion {
private:
template<typename T = void>
InvalidConversion(int, Member<T> = {});
// expected-note@-1 {{in instantiation of template class 'Member<void>' requested here}}
// expected-note@-2 {{passing argument to parameter here}}
};

// expected-note@+1 {{while substituting deduced template arguments into function template 'common_type_sfinae'}}
static_assert(__is_same(decltype(common_type_sfinae<int, InvalidConversion>()), empty_type));
34 changes: 34 additions & 0 deletions clang/test/SemaCXX/type-traits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2673,6 +2673,9 @@ struct FloatWrapper
}
};

template<typename A, typename B, bool result = __is_convertible(A, B)>
static constexpr bool is_convertible_sfinae() { return result; }

void is_convertible()
{
static_assert(__is_convertible(IntWrapper, IntWrapper));
Expand All @@ -2697,6 +2700,10 @@ void is_convertible()
static_assert(__is_convertible(FloatWrapper, const float&));
static_assert(__is_convertible(float, FloatWrapper&&));
static_assert(__is_convertible(float, const FloatWrapper&));

static_assert(!__is_convertible(AllPrivate, AllPrivate));
// Make sure we don't emit "calling a private constructor" in SFINAE context.
static_assert(!is_convertible_sfinae<AllPrivate, AllPrivate>());
}

void is_nothrow_convertible()
Expand Down Expand Up @@ -2822,6 +2829,9 @@ void is_trivial()

template<typename T> struct TriviallyConstructibleTemplate {};

template<typename A, typename B, bool result = __is_assignable(A, B)>
static constexpr bool is_assignable_sfinae() { return result; }

void trivial_checks()
{
static_assert(__is_trivially_copyable(int));
Expand Down Expand Up @@ -2995,6 +3005,10 @@ void trivial_checks()
static_assert(!__is_assignable(AnIncompleteType[1], AnIncompleteType[1])); // expected-error {{incomplete type}}
static_assert(!__is_assignable(void, void));
static_assert(!__is_assignable(const volatile void, const volatile void));

static_assert(!__is_assignable(AllPrivate, AllPrivate));
// Make sure we don't emit "'operator=' is a private member" in SFINAE context.
static_assert(!is_assignable_sfinae<AllPrivate, AllPrivate>());
}

void constructible_checks() {
Expand Down Expand Up @@ -3191,6 +3205,9 @@ void reference_constructs_from_temporary_checks() {

}

template<typename A, typename B, bool result = __reference_converts_from_temporary(A, B)>
static constexpr bool reference_converts_from_temporary_sfinae() { return result; }

void reference_converts_from_temporary_checks() {
static_assert(!__reference_converts_from_temporary(int &, int &));
static_assert(!__reference_converts_from_temporary(int &, int &&));
Expand Down Expand Up @@ -3241,6 +3258,9 @@ void reference_converts_from_temporary_checks() {
static_assert(__reference_converts_from_temporary(const int&, ExplicitConversionRef));
static_assert(__reference_converts_from_temporary(int&&, ExplicitConversionRvalueRef));

static_assert(!__reference_converts_from_temporary(AllPrivate, AllPrivate));
// Make sure we don't emit "calling a private constructor" in SFINAE context.
static_assert(!reference_converts_from_temporary_sfinae<AllPrivate, AllPrivate>());
}

void array_rank() {
Expand Down Expand Up @@ -4085,6 +4105,20 @@ struct NotTriviallyEqualityComparableNonTriviallyEqualityComparableArrs2 {

static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparableNonTriviallyEqualityComparableArrs2));

struct NotTriviallyEqualityComparablePrivateComparison {
int i;

private:
bool operator==(const NotTriviallyEqualityComparablePrivateComparison&) const = default;
};
static_assert(!__is_trivially_equality_comparable(NotTriviallyEqualityComparablePrivateComparison));

template<typename T, bool result = __is_trivially_equality_comparable(T)>
static constexpr bool is_trivially_equality_comparable_sfinae() { return result; }

// Make sure we don't emit "'operator==' is a private member" in SFINAE context.
static_assert(!is_trivially_equality_comparable_sfinae<NotTriviallyEqualityComparablePrivateComparison>());

template<bool B>
struct MaybeTriviallyEqualityComparable {
int i;
Expand Down
Loading