Skip to content
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
1 change: 1 addition & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,7 @@ Bug Fixes to C++ Support
object type. (#GH151531)
- Suppress ``-Wdouble-promotion`` when explicitly asked for with C++ list initialization (#GH33409).
- Fix the result of `__builtin_is_implicit_lifetime` for types with a user-provided constructor. (#GH160610)
- Correctly deduce return types in ``decltype`` expressions. (#GH160497) (#GH56652) (#GH116319) (#GH161196)

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20108,8 +20108,9 @@ static void DoMarkVarDeclReferenced(
bool NeededForConstantEvaluation =
isPotentiallyConstantEvaluatedContext(SemaRef) && UsableInConstantExpr;

bool NeedDefinition =
OdrUse == OdrUseContext::Used || NeededForConstantEvaluation;
bool NeedDefinition = OdrUse == OdrUseContext::Used ||
NeededForConstantEvaluation ||
Var->getType()->isUndeducedType();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually wonder if this should be isUndeducedAutoType. This ends up doing a type visitor to check if there is ANY 'auto' in the type (GetContainedDeducedTypeVisitor).

Do we expect this to work with an auto* return type to the lambda, etc? I actually lean towards YES, but would love a test or two?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we condition this check on TSK != TSK_Undeclared? As it stands, every time any variable is mentioned we'll walk its type looking for auto, which sounds quite expensive, given how common name references to variables are.

Copy link
Contributor

@zyn0217 zyn0217 Sep 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, note that we do have the same check inside this function

  if (UsableInConstantExpr || Var->getType()->isUndeducedType()) {

So it would be at least worth combining it into a bool variable.

@cor3ntin

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just saw that, I'll make a PR


assert(!isa<VarTemplatePartialSpecializationDecl>(Var) &&
"Can't instantiate a partial template specialization.");
Expand Down
41 changes: 41 additions & 0 deletions clang/test/CodeGenCXX/gh56652.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// RUN: %clang_cc1 -std=c++20 -triple x86_64-elf-gnu %s -emit-llvm -o - | FileCheck %s

namespace GH56652{

struct foo {};

template <typename T> struct bar {
using type = T;

template <foo> inline static constexpr auto b = true;
};

template <typename T>
concept C = requires(T a) { T::template b<foo{}>; };

template <typename T> auto fn(T) {
if constexpr (!C<T>)
return foo{};
else
return T{};
}

auto a = decltype(fn(bar<int>{})){};

}

namespace GH116319 {

template <int = 0> struct a {
template <class> static constexpr auto b = 2;
template <class> static void c() noexcept(noexcept(b<int>)) {}
};

void test() { a<>::c<int>(); }


}

// CHECK: %"struct.GH56652::bar" = type { i8 }
// CHECK: $_ZN8GH1163191aILi0EE1cIiEEvv = comdat any
// CHECK: @_ZN7GH566521aE = global %"struct.GH56652::bar" undef
78 changes: 78 additions & 0 deletions clang/test/SemaCXX/decltype.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify -Wno-c99-designator %s
// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify -Wno-c99-designator %s

// PR5290
int const f0();
Expand Down Expand Up @@ -156,13 +157,90 @@ struct A {
}
};



// This shouldn't crash.
static_assert(A<int>().f<int>() == 0, "");
// The result should not be dependent.
static_assert(A<int>().f<int>() != 0, ""); // expected-error {{static assertion failed due to requirement 'GH99873::A<int>().f<int>() != 0'}}
// expected-note@-1 {{expression evaluates to '0 != 0'}}
}


#if __cplusplus >= 201703L
namespace GH160497 {

template <class> struct S {
template <class>
inline static auto mem =
[] { static_assert(false); // expected-error {{static assertion failed}} \
// expected-note {{while substituting into a lambda expression here}}
return 42;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test ONLY tests auto as a return, but the change itself is ANY type containing an undeduced type. Perhaps there is value in a -> auto *, ->auto [], function pointer return, and `pack expansion' type?

}();
};

using T = decltype(S<void>::mem<void>);
// expected-note@-1 {{in instantiation of static data member 'GH160497::S<void>::mem<void>' requested here}}


template <class> struct S2 {
template <class>
inline static auto* mem =
[] { static_assert(false); // expected-error {{static assertion failed}} \
// expected-note {{while substituting into a lambda expression here}}
return static_cast<int*>(nullptr);
}();
};

using T2 = decltype(S2<void>::mem<void>);
//expected-note@-1 {{in instantiation of static data member 'GH160497::S2<void>::mem<void>' requested here}}

template <class> struct S3 {
template <class>
inline static int mem = // Check we don't instantiate when the type is not deduced.
[] { static_assert(false);
return 42;
}();
};

using T = decltype(S3<void>::mem<void>);
}

namespace N1 {

template<class>
struct S {
template<class>
inline static auto mem = 42;
};

using T = decltype(S<void>::mem<void>);

T y = 42;

}

namespace GH161196 {

template <typename> struct A {
static constexpr int digits = 0;
};

template <typename> struct B {
template <int, typename MaskInt = int, int = A<MaskInt>::digits>
static constexpr auto XBitMask = 0;
};

struct C {
using ReferenceHost = B<int>;
template <int> static decltype(ReferenceHost::XBitMask<0>) XBitMask;
};

void test() { (void)C::XBitMask<0>; }

}
#endif

template<typename>
class conditional {
};
Expand Down