Skip to content

Commit

Permalink
[c++20] P1907R1: Support for generalized non-type template arguments …
Browse files Browse the repository at this point in the history
…of scalar type. (#78041)

Previously committed as 9e08e51, and
reverted because a dependency commit was reverted, then committed again
as 4b57400 and reverted again because
"dependency commit" 5a391d3 was
reverted. But it doesn't seem that 5a391d3 was a real dependency
for this.

This commit incorporates 4b57400 and
18e093f by Richard Smith (@zygoloid),
with some minor fixes, most notably:

- `UncommonValue` renamed to `StructuralValue`

- `VK_PRValue` instead of `VK_RValue` as default kind in lvalue and
member pointer handling branch in
`BuildExpressionFromNonTypeTemplateArgumentValue`;

- handling of `StructuralValue` in `IsTypeDeclaredInsideVisitor`;

- filling in `SugaredConverted` along with `CanonicalConverted`
parameter in `Sema::CheckTemplateArgument`;

- minor cleanup in
`TemplateInstantiator::transformNonTypeTemplateParmRef`;

- `TemplateArgument` constructors refactored;

- `ODRHash` calculation for `UncommonValue`;

- USR generation for `UncommonValue`;

- more correct MS compatibility mangling algorithm (tested on MSVC ver.
19.35; toolset ver. 143);

- IR emitting fixed on using a subobject as a template argument when the
corresponding template parameter is used in an lvalue context;

- `noundef` attribute and opaque pointers in `template-arguments` test;

- analysis for C++17 mode is turned off for templates in
`warn-bool-conversion` test; in C++17 and C++20 mode, array reference
used as a template argument of pointer type produces template argument
of UncommonValue type, and
`BuildExpressionFromNonTypeTemplateArgumentValue` makes
`OpaqueValueExpr` for it, and `DiagnoseAlwaysNonNullPointer` cannot see
through it; despite of "These cases should not warn" comment, I'm not
sure about correct behavior; I'd expect a suggestion to replace `if` by
`if constexpr`;

- `temp.arg.nontype/p1.cpp` and `dr18xx.cpp` tests fixed.
  • Loading branch information
bolshakov-a committed Jan 21, 2024
1 parent 30d6806 commit 5518a9d
Show file tree
Hide file tree
Showing 47 changed files with 1,109 additions and 230 deletions.
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/DumpAST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ class DumpVisitor : public RecursiveASTVisitor<DumpVisitor> {
TEMPLATE_ARGUMENT_KIND(Declaration);
TEMPLATE_ARGUMENT_KIND(Template);
TEMPLATE_ARGUMENT_KIND(TemplateExpansion);
TEMPLATE_ARGUMENT_KIND(StructuralValue);
#undef TEMPLATE_ARGUMENT_KIND
}
llvm_unreachable("Unhandled ArgKind enum");
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/clangd/FindTarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1039,6 +1039,7 @@ class ExplicitReferenceCollector
case TemplateArgument::Pack:
case TemplateArgument::Type:
case TemplateArgument::Expression:
case TemplateArgument::StructuralValue:
break; // Handled by VisitType and VisitExpression.
};
return RecursiveASTVisitor::TraverseTemplateArgumentLoc(A);
Expand Down
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,9 @@ C++ Language Changes

C++20 Feature Support
^^^^^^^^^^^^^^^^^^^^^
- Implemented `P1907R1 <https://wg21.link/P1907R1>` which extends allowed non-type template argument
kinds with e.g. floating point values and pointers and references to subobjects.
This feature is still experimental. Accordingly, `__cpp_nontype_template_args` was not updated.

C++23 Feature Support
^^^^^^^^^^^^^^^^^^^^^
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/AST/ODRHash.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

namespace clang {

class APValue;
class Decl;
class IdentifierInfo;
class NestedNameSpecifier;
Expand Down Expand Up @@ -101,6 +102,8 @@ class ODRHash {
// Save booleans until the end to lower the size of data to process.
void AddBoolean(bool value);

void AddStructuralValue(const APValue &);

static bool isSubDeclToBeProcessed(const Decl *D, const DeclContext *Parent);

private:
Expand Down
14 changes: 14 additions & 0 deletions clang/include/clang/AST/PropertiesBase.td
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,20 @@ let Class = PropertyTypeCase<TemplateArgument, "Integral"> in {
return TemplateArgument(ctx, value, type, isDefaulted);
}]>;
}
let Class = PropertyTypeCase<TemplateArgument, "StructuralValue"> in {
def : Property<"value", APValue> {
let Read = [{ node.getAsStructuralValue() }];
}
def : Property<"type", QualType> {
let Read = [{ node.getStructuralValueType() }];
}
def : Property<"isDefaulted", Bool> {
let Read = [{ node.getIsDefaulted() }];
}
def : Creator<[{
return TemplateArgument(ctx, type, value, isDefaulted);
}]>;
}
let Class = PropertyTypeCase<TemplateArgument, "Template"> in {
def : Property<"name", TemplateName> {
let Read = [{ node.getAsTemplateOrTemplatePattern() }];
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,7 @@ bool RecursiveASTVisitor<Derived>::TraverseTemplateArgument(
case TemplateArgument::Declaration:
case TemplateArgument::Integral:
case TemplateArgument::NullPtr:
case TemplateArgument::StructuralValue:
return true;

case TemplateArgument::Type:
Expand Down Expand Up @@ -882,6 +883,7 @@ bool RecursiveASTVisitor<Derived>::TraverseTemplateArgumentLoc(
case TemplateArgument::Declaration:
case TemplateArgument::Integral:
case TemplateArgument::NullPtr:
case TemplateArgument::StructuralValue:
return true;

case TemplateArgument::Type: {
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/AST/TemplateArgumentVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class Base {
DISPATCH(Declaration);
DISPATCH(NullPtr);
DISPATCH(Integral);
DISPATCH(StructuralValue);
DISPATCH(Template);
DISPATCH(TemplateExpansion);
DISPATCH(Expression);
Expand All @@ -59,6 +60,7 @@ class Base {
VISIT_METHOD(Declaration);
VISIT_METHOD(NullPtr);
VISIT_METHOD(Integral);
VISIT_METHOD(StructuralValue);
VISIT_METHOD(Template);
VISIT_METHOD(TemplateExpansion);
VISIT_METHOD(Expression);
Expand Down
86 changes: 55 additions & 31 deletions clang/include/clang/AST/TemplateBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ template <> struct PointerLikeTypeTraits<clang::Expr *> {

namespace clang {

class APValue;
class ASTContext;
class Expr;
struct PrintingPolicy;
Expand Down Expand Up @@ -80,6 +81,13 @@ class TemplateArgument {
/// that was provided for an integral non-type template parameter.
Integral,

/// The template argument is a non-type template argument that can't be
/// represented by the special-case Declaration, NullPtr, or Integral
/// forms. These values are only ever produced by constant evaluation,
/// so cannot be dependent.
/// TODO: merge Declaration, NullPtr and Integral into this?
StructuralValue,

/// The template argument is a template name that was provided for a
/// template template parameter.
Template,
Expand Down Expand Up @@ -130,6 +138,14 @@ class TemplateArgument {
};
void *Type;
};
struct V {
LLVM_PREFERRED_TYPE(ArgKind)
unsigned Kind : 31;
LLVM_PREFERRED_TYPE(bool)
unsigned IsDefaulted : 1;
APValue *Value;
void *Type;
};
struct A {
LLVM_PREFERRED_TYPE(ArgKind)
unsigned Kind : 31;
Expand All @@ -156,37 +172,42 @@ class TemplateArgument {
union {
struct DA DeclArg;
struct I Integer;
struct V Value;
struct A Args;
struct TA TemplateArg;
struct TV TypeOrValue;
};

void initFromType(QualType T, bool IsNullPtr, bool IsDefaulted);
void initFromDeclaration(ValueDecl *D, QualType QT, bool IsDefaulted);
void initFromIntegral(const ASTContext &Ctx, const llvm::APSInt &Value,
QualType Type, bool IsDefaulted);
void initFromStructural(const ASTContext &Ctx, QualType Type,
const APValue &V, bool IsDefaulted);

public:
/// Construct an empty, invalid template argument.
constexpr TemplateArgument() : TypeOrValue({Null, 0, /* IsDefaulted */ 0}) {}

/// Construct a template type argument.
TemplateArgument(QualType T, bool isNullPtr = false,
bool IsDefaulted = false) {
TypeOrValue.Kind = isNullPtr ? NullPtr : Type;
TypeOrValue.IsDefaulted = IsDefaulted;
TypeOrValue.V = reinterpret_cast<uintptr_t>(T.getAsOpaquePtr());
initFromType(T, isNullPtr, IsDefaulted);
}

/// Construct a template argument that refers to a
/// declaration, which is either an external declaration or a
/// template declaration.
/// Construct a template argument that refers to a (non-dependent)
/// declaration.
TemplateArgument(ValueDecl *D, QualType QT, bool IsDefaulted = false) {
assert(D && "Expected decl");
DeclArg.Kind = Declaration;
DeclArg.IsDefaulted = IsDefaulted;
DeclArg.QT = QT.getAsOpaquePtr();
DeclArg.D = D;
initFromDeclaration(D, QT, IsDefaulted);
}

/// Construct an integral constant template argument. The memory to
/// store the value is allocated with Ctx.
TemplateArgument(ASTContext &Ctx, const llvm::APSInt &Value, QualType Type,
TemplateArgument(const ASTContext &Ctx, const llvm::APSInt &Value,
QualType Type, bool IsDefaulted = false);

/// Construct a template argument from an arbitrary constant value.
TemplateArgument(const ASTContext &Ctx, QualType Type, const APValue &Value,
bool IsDefaulted = false);

/// Construct an integral constant template argument with the same
Expand Down Expand Up @@ -297,7 +318,7 @@ class TemplateArgument {
/// Retrieve the type for a type template argument.
QualType getAsType() const {
assert(getKind() == Type && "Unexpected kind");
return QualType::getFromOpaquePtr(reinterpret_cast<void*>(TypeOrValue.V));
return QualType::getFromOpaquePtr(reinterpret_cast<void *>(TypeOrValue.V));
}

/// Retrieve the declaration for a declaration non-type
Expand All @@ -315,7 +336,7 @@ class TemplateArgument {
/// Retrieve the type for null non-type template argument.
QualType getNullPtrType() const {
assert(getKind() == NullPtr && "Unexpected kind");
return QualType::getFromOpaquePtr(reinterpret_cast<void*>(TypeOrValue.V));
return QualType::getFromOpaquePtr(reinterpret_cast<void *>(TypeOrValue.V));
}

/// Retrieve the template name for a template name argument.
Expand Down Expand Up @@ -371,6 +392,14 @@ class TemplateArgument {
/// default template parameter.
bool getIsDefaulted() const { return (bool)TypeOrValue.IsDefaulted; }

/// Get the value of a StructuralValue.
const APValue &getAsStructuralValue() const { return *Value.Value; }

/// Get the type of a StructuralValue.
QualType getStructuralValueType() const {
return QualType::getFromOpaquePtr(Value.Type);
}

/// If this is a non-type template argument, get its type. Otherwise,
/// returns a null QualType.
QualType getNonTypeTemplateArgumentType() const;
Expand Down Expand Up @@ -516,6 +545,7 @@ class TemplateArgumentLoc {
assert(Argument.getKind() == TemplateArgument::NullPtr ||
Argument.getKind() == TemplateArgument::Integral ||
Argument.getKind() == TemplateArgument::Declaration ||
Argument.getKind() == TemplateArgument::StructuralValue ||
Argument.getKind() == TemplateArgument::Expression);
}

Expand All @@ -541,13 +571,9 @@ class TemplateArgumentLoc {
/// - Fetches the full source range of the argument.
SourceRange getSourceRange() const LLVM_READONLY;

const TemplateArgument &getArgument() const {
return Argument;
}
const TemplateArgument &getArgument() const { return Argument; }

TemplateArgumentLocInfo getLocInfo() const {
return LocInfo;
}
TemplateArgumentLocInfo getLocInfo() const { return LocInfo; }

TypeSourceInfo *getTypeSourceInfo() const {
if (Argument.getKind() != TemplateArgument::Type)
Expand Down Expand Up @@ -575,6 +601,11 @@ class TemplateArgumentLoc {
return LocInfo.getAsExpr();
}

Expr *getSourceStructuralValueExpression() const {
assert(Argument.getKind() == TemplateArgument::StructuralValue);
return LocInfo.getAsExpr();
}

NestedNameSpecifierLoc getTemplateQualifierLoc() const {
if (Argument.getKind() != TemplateArgument::Template &&
Argument.getKind() != TemplateArgument::TemplateExpansion)
Expand Down Expand Up @@ -606,8 +637,7 @@ class TemplateArgumentListInfo {
public:
TemplateArgumentListInfo() = default;

TemplateArgumentListInfo(SourceLocation LAngleLoc,
SourceLocation RAngleLoc)
TemplateArgumentListInfo(SourceLocation LAngleLoc, SourceLocation RAngleLoc)
: LAngleLoc(LAngleLoc), RAngleLoc(RAngleLoc) {}

// This can leak if used in an AST node, use ASTTemplateArgumentListInfo
Expand All @@ -626,21 +656,15 @@ class TemplateArgumentListInfo {
return Arguments.data();
}

llvm::ArrayRef<TemplateArgumentLoc> arguments() const {
return Arguments;
}
llvm::ArrayRef<TemplateArgumentLoc> arguments() const { return Arguments; }

const TemplateArgumentLoc &operator[](unsigned I) const {
return Arguments[I];
}

TemplateArgumentLoc &operator[](unsigned I) {
return Arguments[I];
}
TemplateArgumentLoc &operator[](unsigned I) { return Arguments[I]; }

void addArgument(const TemplateArgumentLoc &Loc) {
Arguments.push_back(Loc);
}
void addArgument(const TemplateArgumentLoc &Loc) { Arguments.push_back(Loc); }
};

/// Represents an explicit template argument list in C++, e.g.,
Expand Down
5 changes: 0 additions & 5 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -5154,8 +5154,6 @@ def err_non_type_template_arg_subobject : Error<
"non-type template argument refers to subobject '%0'">;
def err_non_type_template_arg_addr_label_diff : Error<
"template argument / label address difference / what did you expect?">;
def err_non_type_template_arg_unsupported : Error<
"non-type template argument of type %0 is not yet supported">;
def err_template_arg_not_convertible : Error<
"non-type template argument of type %0 cannot be converted to a value "
"of type %1">;
Expand Down Expand Up @@ -5207,9 +5205,6 @@ def err_template_arg_not_object_or_func : Error<
"non-type template argument does not refer to an object or function">;
def err_template_arg_not_pointer_to_member_form : Error<
"non-type template argument is not a pointer to member constant">;
def err_template_arg_member_ptr_base_derived_not_supported : Error<
"non-type template argument of pointer-to-member type %1 that refers "
"to member %q0 of a different class is not supported yet">;
def err_template_arg_invalid : Error<
"non-type template argument '%0' is invalid">;
def ext_template_arg_extra_parens : ExtWarn<
Expand Down
4 changes: 2 additions & 2 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -8595,8 +8595,8 @@ class Sema final {
QualType ParamType,
SourceLocation Loc);
ExprResult
BuildExpressionFromIntegralTemplateArgument(const TemplateArgument &Arg,
SourceLocation Loc);
BuildExpressionFromNonTypeTemplateArgument(const TemplateArgument &Arg,
SourceLocation Loc);

/// Enumeration describing how template parameter lists are compared
/// for equality.
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6754,6 +6754,11 @@ ASTContext::getCanonicalTemplateArgument(const TemplateArgument &Arg) const {
case TemplateArgument::Integral:
return TemplateArgument(Arg, getCanonicalType(Arg.getIntegralType()));

case TemplateArgument::StructuralValue:
return TemplateArgument(*this,
getCanonicalType(Arg.getStructuralValueType()),
Arg.getAsStructuralValue());

case TemplateArgument::Type:
return TemplateArgument(getCanonicalType(Arg.getAsType()),
/*isNullPtr*/ false, Arg.getIsDefaulted());
Expand Down
13 changes: 13 additions & 0 deletions clang/lib/AST/ASTImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,17 @@ ASTNodeImporter::import(const TemplateArgument &From) {
From.getIsDefaulted());
}

case TemplateArgument::StructuralValue: {
ExpectedType ToTypeOrErr = import(From.getStructuralValueType());
if (!ToTypeOrErr)
return ToTypeOrErr.takeError();
Expected<APValue> ToValueOrErr = import(From.getAsStructuralValue());
if (!ToValueOrErr)
return ToValueOrErr.takeError();
return TemplateArgument(Importer.getToContext(), *ToTypeOrErr,
*ToValueOrErr);
}

case TemplateArgument::Template: {
Expected<TemplateName> ToTemplateOrErr = import(From.getAsTemplate());
if (!ToTemplateOrErr)
Expand Down Expand Up @@ -3572,6 +3583,8 @@ class IsTypeDeclaredInsideVisitor
case TemplateArgument::NullPtr:
// FIXME: The type is not allowed to be in the function?
return CheckType(Arg.getNullPtrType());
case TemplateArgument::StructuralValue:
return CheckType(Arg.getStructuralValueType());
case TemplateArgument::Pack:
for (const auto &PackArg : Arg.getPackAsArray())
if (checkTemplateArgument(PackArg))
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/ASTStructuralEquivalence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,9 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
return IsStructurallyEquivalent(Context, Arg1.getAsExpr(),
Arg2.getAsExpr());

case TemplateArgument::StructuralValue:
return Arg1.structurallyEquals(Arg2);

case TemplateArgument::Pack:
return IsStructurallyEquivalent(Context, Arg1.pack_elements(),
Arg2.pack_elements());
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,10 @@ LinkageComputer::getLVForTemplateArgumentList(ArrayRef<TemplateArgument> Args,
LV.merge(getTypeLinkageAndVisibility(Arg.getNullPtrType()));
continue;

case TemplateArgument::StructuralValue:
LV.merge(getLVForValue(Arg.getAsStructuralValue(), computation));
continue;

case TemplateArgument::Template:
case TemplateArgument::TemplateExpansion:
if (TemplateDecl *Template =
Expand Down

0 comments on commit 5518a9d

Please sign in to comment.