courbet updated this revision to Diff 175673.
courbet added a comment.
Add unit test with dependent types.
Repository:
rC Clang
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D54903/new/
https://reviews.llvm.org/D54903
Files:
lib/Sema/SemaTemplate.cpp
test/SemaCXX/static-assert.cpp
Index: test/SemaCXX/static-assert.cpp
===================================================================
--- test/SemaCXX/static-assert.cpp
+++ test/SemaCXX/static-assert.cpp
@@ -68,3 +68,58 @@
};
static_assert(first_trait<X>::value && second_trait<X>::value, "message"); // expected-error{{static_assert failed due to requirement 'second_trait<X>::value' "message"}}
+
+namespace std {
+
+template <class Tp, Tp v>
+struct integral_constant {
+ static const Tp value = v;
+ typedef Tp value_type;
+ typedef integral_constant type;
+};
+
+template <class Tp, Tp v>
+const Tp integral_constant<Tp, v>::value;
+
+typedef integral_constant<bool, true> true_type;
+typedef integral_constant<bool, false> false_type;
+
+template <class Tp>
+struct is_const : public false_type {};
+template <class Tp>
+struct is_const<Tp const> : public true_type {};
+
+// We do not define is_same in terms of integral_constant to check that both implementations are supported.
+template <typename T, typename U>
+struct is_same {
+ static const bool value = false;
+};
+
+template <typename T>
+struct is_same<T, T> {
+ static const bool value = true;
+};
+
+} // namespace std
+
+struct ExampleTypes {
+ using T = int;
+ using U = float;
+};
+
+static_assert(std::is_same<ExampleTypes::T, ExampleTypes::U>::value, "message"); // expected-error{{static_assert failed due to requirement 'std::is_same<int, float>::value' "message"}}
+static_assert(std::is_const<ExampleTypes::T>::value, "message"); // expected-error{{static_assert failed due to requirement 'std::is_const<int>::value' "message"}}
+
+struct BI_tag {};
+struct RAI_tag : BI_tag {};
+struct MyIterator {
+ using tag = BI_tag;
+};
+struct MyContainer {
+ using iterator = MyIterator;
+};
+template <class Container>
+void foo() {
+ static_assert(std::is_same<RAI_tag, typename Container::iterator::tag>::value, "message"); // expected-error{{static_assert failed due to requirement 'std::is_same<RAI_tag, BI_tag>::value' "message"}}
+}
+template void foo<MyContainer>(); // expected-note {{in instantiation of function template specialization 'foo<MyContainer>' requested here}}
Index: lib/Sema/SemaTemplate.cpp
===================================================================
--- lib/Sema/SemaTemplate.cpp
+++ lib/Sema/SemaTemplate.cpp
@@ -3052,6 +3052,69 @@
return Cond;
}
+// Returns true if `RecordName` is the name of an std type trait.
+static bool isTypeTraitName(const llvm::StringRef RecordName) {
+ return
+#define TYPE_TRAIT_1(Spelling, Name, Key) RecordName == (#Spelling + 2) ||
+#define TYPE_TRAIT_2(Spelling, Name, Key) RecordName == (#Spelling + 2) ||
+#define TYPE_TRAIT_N(Spelling, Name, Key) RecordName == (#Spelling + 2) ||
+#include "clang/Basic/TokenKinds.def"
+#undef TYPE_TRAIT_1
+#undef TYPE_TRAIT_2
+#undef TYPE_TRAIT_N
+ false;
+}
+
+// If `Record` is a type trait, pretty prints the condition with actual types
+// and returns true.
+static bool prettyPrintTypeTrait(const NestedNameSpecifier *NNS,
+ llvm::raw_string_ostream &OS,
+ const PrintingPolicy &PrintPolicy) {
+ // We are looking for a type.
+ if (NNS == nullptr || NNS->getKind() != NestedNameSpecifier::TypeSpec)
+ return false;
+ const RecordDecl *Record = NNS->getAsRecordDecl();
+ if (Record == nullptr)
+ return false;
+ // In namespace "::std".
+ if (!Record->isInStdNamespace())
+ return false;
+ // Now check whether the record name is one of the known type traits.
+ if (!isTypeTraitName(Record->getName()))
+ return false;
+ // Print the type trait with resolved template parameters.
+ const auto *TmplDecl = dyn_cast<ClassTemplateSpecializationDecl>(Record);
+ assert(TmplDecl &&
+ "std type_traits should be ClassTemplateSpecializationDecl");
+ OS << "std::" << Record->getName() << "<";
+ ArrayRef<TemplateArgument> Args = TmplDecl->getTemplateArgs().asArray();
+ Args.front().print(PrintPolicy, OS);
+ for (const auto &Arg : Args.drop_front()) {
+ OS << ", ";
+ Arg.print(PrintPolicy, OS);
+ }
+ OS << ">";
+ return true;
+}
+
+// Print a diagnostic for the failing static_assert expression. Defaults to
+// pretty-printing the expression.
+static void
+prettyPrintFailedBooleanCondition(llvm::raw_string_ostream &OS,
+ const Expr *FailedCond,
+ const PrintingPolicy &PrintPolicy) {
+ if (const auto *DR = dyn_cast<DeclRefExpr>(FailedCond)) {
+ const auto *Var = dyn_cast<VarDecl>(DR->getDecl());
+ // This might be `std::some_type_trait<U,V>::value`.
+ if (Var && Var->isStaticDataMember() && Var->getName() == "value" &&
+ prettyPrintTypeTrait(DR->getQualifier(), OS, PrintPolicy)) {
+ OS << "::value";
+ return;
+ }
+ }
+ FailedCond->printPretty(OS, nullptr, PrintPolicy);
+}
+
std::pair<Expr *, std::string>
Sema::findFailedBooleanCondition(Expr *Cond, bool AllowTopLevelCond) {
Cond = lookThroughRangesV3Condition(PP, Cond);
@@ -3093,7 +3156,7 @@
std::string Description;
{
llvm::raw_string_ostream Out(Description);
- FailedCond->printPretty(Out, nullptr, getPrintingPolicy());
+ prettyPrintFailedBooleanCondition(Out, FailedCond, getPrintingPolicy());
}
return { FailedCond, Description };
}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits