Skip to content

[clang] Avoid crash due to unimplemented StructuralValue support in the template differ #93265

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 1 commit into from
May 24, 2024
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 @@ -790,6 +790,7 @@ Miscellaneous Clang Crashes Fixed

- Do not attempt to dump the layout of dependent types or invalid declarations
when ``-fdump-record-layouts-complete`` is passed. Fixes #GH83684.
- Unhandled StructuralValues in the template differ (#GH93068).

OpenACC Specific Changes
------------------------
Expand Down
106 changes: 64 additions & 42 deletions clang/lib/AST/ASTDiagnostic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1215,46 +1215,19 @@ class TemplateDiff {
bool &NeedAddressOf) {
if (!Iter.isEnd()) {
switch (Iter->getKind()) {
default:
llvm_unreachable("unknown ArgumentKind");
case TemplateArgument::Integral:
Value = Iter->getAsIntegral();
HasInt = true;
IntType = Iter->getIntegralType();
return;
case TemplateArgument::Declaration: {
VD = Iter->getAsDecl();
QualType ArgType = Iter->getParamTypeForDecl();
QualType VDType = VD->getType();
if (ArgType->isPointerType() &&
Context.hasSameType(ArgType->getPointeeType(), VDType))
NeedAddressOf = true;
return;
}
case TemplateArgument::NullPtr:
IsNullPtr = true;
return;
case TemplateArgument::Expression:
E = Iter->getAsExpr();
}
} else if (!Default->isParameterPack()) {
E = Default->getDefaultArgument().getArgument().getAsExpr();
}

if (!Iter.hasDesugaredTA()) return;

const TemplateArgument& TA = Iter.getDesugaredTA();
switch (TA.getKind()) {
default:
llvm_unreachable("unknown ArgumentKind");
case TemplateArgument::StructuralValue:
// FIXME: Diffing of structural values is not implemented.
// There is no possible fallback in this case, this will show up
// as '(no argument)'.
return;
case TemplateArgument::Integral:
Value = TA.getAsIntegral();
Value = Iter->getAsIntegral();
HasInt = true;
IntType = TA.getIntegralType();
IntType = Iter->getIntegralType();
return;
case TemplateArgument::Declaration: {
VD = TA.getAsDecl();
QualType ArgType = TA.getParamTypeForDecl();
VD = Iter->getAsDecl();
QualType ArgType = Iter->getParamTypeForDecl();
QualType VDType = VD->getType();
if (ArgType->isPointerType() &&
Context.hasSameType(ArgType->getPointeeType(), VDType))
Expand All @@ -1265,13 +1238,62 @@ class TemplateDiff {
IsNullPtr = true;
return;
case TemplateArgument::Expression:
// TODO: Sometimes, the desugared template argument Expr differs from
// the sugared template argument Expr. It may be useful in the future
// but for now, it is just discarded.
if (!E)
E = TA.getAsExpr();
return;
E = Iter->getAsExpr();
break;
case TemplateArgument::Null:
case TemplateArgument::Type:
case TemplateArgument::Template:
case TemplateArgument::TemplateExpansion:
llvm_unreachable("TemplateArgument kind is not expected for NTTP");
case TemplateArgument::Pack:
llvm_unreachable("TemplateArgument kind should be handled elsewhere");
}
} else if (!Default->isParameterPack()) {
E = Default->getDefaultArgument().getArgument().getAsExpr();
}

if (!Iter.hasDesugaredTA())
return;

const TemplateArgument &TA = Iter.getDesugaredTA();
switch (TA.getKind()) {
case TemplateArgument::StructuralValue:
// FIXME: Diffing of structural values is not implemented.
// Just fall back to the expression.
return;
case TemplateArgument::Integral:
Value = TA.getAsIntegral();
HasInt = true;
IntType = TA.getIntegralType();
return;
case TemplateArgument::Declaration: {
VD = TA.getAsDecl();
QualType ArgType = TA.getParamTypeForDecl();
QualType VDType = VD->getType();
if (ArgType->isPointerType() &&
Context.hasSameType(ArgType->getPointeeType(), VDType))
NeedAddressOf = true;
return;
}
case TemplateArgument::NullPtr:
IsNullPtr = true;
return;
case TemplateArgument::Expression:
// TODO: Sometimes, the desugared template argument Expr differs from
// the sugared template argument Expr. It may be useful in the future
// but for now, it is just discarded.
if (!E)
E = TA.getAsExpr();
return;
case TemplateArgument::Null:
case TemplateArgument::Type:
case TemplateArgument::Template:
case TemplateArgument::TemplateExpansion:
llvm_unreachable("TemplateArgument kind is not expected for NTTP");
case TemplateArgument::Pack:
llvm_unreachable("TemplateArgument kind should be handled elsewhere");
}
llvm_unreachable("Unexpected TemplateArgument kind");
}

/// DiffNonTypes - Handles any template parameters not handled by DiffTypes
Expand Down
49 changes: 49 additions & 0 deletions clang/test/Misc/diag-template-diffing-cxx26.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// RUN: %clang_cc1 -fsyntax-only %s -std=c++26 -verify=expected,notree
// RUN: %clang_cc1 -fsyntax-only %s -std=c++26 -fno-elide-type -verify=expected,notree
// RUN: %clang_cc1 -fsyntax-only %s -std=c++26 -fdiagnostics-show-template-tree -verify=expected,tree
// RUN: %clang_cc1 -fsyntax-only %s -std=c++26 -fno-elide-type -fdiagnostics-show-template-tree -verify=expected,tree

namespace GH93068 {
int n[2];

template <auto> struct A {}; // #A

namespace t1 {
// notree-error@#1 {{no viable conversion from 'A<0>' to 'A<n + 1>'}}

/* tree-error@#1 {{no viable conversion
A<
[0 != n + 1]>}}*/

A<n + 1> v1 = A<0>(); // #1
// expected-note@#A {{no known conversion from 'A<0>' to 'const A<&n[1]> &' for 1st argument}}
// expected-note@#A {{no known conversion from 'A<0>' to 'A<&n[1]> &&' for 1st argument}}

// notree-error@#2 {{no viable conversion from 'A<n>' to 'A<(no argument)>'}}
/* tree-error@#2 {{no viable conversion
A<
[n != (no argument)]>}}*/

A<n + 1> v2 = A<n>(); // #2
// expected-note@#A {{no known conversion from 'A<n>' to 'const A<&n[1]> &' for 1st argument}}
// expected-note@#A {{no known conversion from 'A<n>' to 'A<&n[1]> &&' for 1st argument}}
} // namespace t1

namespace t2 {
A<n> v1;
A<n + 1> v2;

// notree-note@#A {{no known conversion from 'A<n>' to 'const A<(no argument)>' for 1st argument}}
// notree-note@#A {{no known conversion from 'A<n>' to 'A<(no argument)>' for 1st argument}}

/* tree-note@#A {{no known conversion from argument type to parameter type for 1st argument
[(no qualifiers) != const] A<
[n != (no argument)]>}}*/

/* tree-note@#A {{no known conversion from argument type to parameter type for 1st argument
A<
[n != (no argument)]>}}*/

void f() { v2 = v1; } // expected-error {{no viable overloaded '='}}
} // namespace t2
} // namespace GH93068
Loading