From 969c689d893a248eca4f049f5b89f747e66e4bff Mon Sep 17 00:00:00 2001
From: Douglas Gregor
Date: Wed, 1 Apr 2009 15:47:24 +0000
Subject: [PATCH] Give Type::getDesugaredType a "for-display" mode that can
apply more heuristics to determine when it's useful to desugar a type for
display to the user. Introduce two C++-specific heuristics:
- For a qualified type (like "foo::bar"), only produce a new
desugred type if desugaring the qualified type ("bar", in this
case) produces something interesting. For example, if "foo::bar"
refers to a class named "bar", don't desugar. However, if
"foo::bar" refers to a typedef of something else, desugar to that
something else. This gives some useful desugaring such as
"foo::bar (aka 'int')".
- Don't desugar class template specialization types like
"basic_string" down to their underlying "class
basic_string, allocator>, etc.";
it's better just to leave such types alone.
Update diagnostics.html with some discussion and examples of type
preservation in C++, showing qualified names and class template
specialization types.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@68207 91177308-0d34-0410-b5e6-96231b3b80d8
---
include/clang/AST/Type.h | 4 +-
lib/AST/Type.cpp | 34 ++++++++++++----
lib/Sema/Sema.cpp | 2 +-
test/SemaCXX/qualified-names-diag.cpp | 6 +--
test/SemaTemplate/qualified-names-diag.cpp | 2 +-
www/diagnostics.html | 47 +++++++++++++++++++---
6 files changed, 76 insertions(+), 19 deletions(-)
diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h
index c3195eb668..514c69e72c 100644
--- a/include/clang/AST/Type.h
+++ b/include/clang/AST/Type.h
@@ -162,7 +162,7 @@ public:
/// to getting the canonical type, but it doesn't remove *all* typedefs. For
/// example, it returns "T*" as "T*", (not as "int*"), because the pointer is
/// concrete.
- QualType getDesugaredType() const;
+ QualType getDesugaredType(bool ForDisplay = false) const;
/// operator==/!= - Indicate whether the specified types and qualifiers are
/// identical.
@@ -461,7 +461,7 @@ public:
/// to getting the canonical type, but it doesn't remove *all* typedefs. For
/// example, it returns "T*" as "T*", (not as "int*"), because the pointer is
/// concrete.
- QualType getDesugaredType() const;
+ QualType getDesugaredType(bool ForDisplay = false) const;
/// More type predicates useful for type checking/promotion
bool isPromotableIntegerType() const; // C99 6.3.1.1p2
diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp
index b9bd0bae04..97245c699a 100644
--- a/lib/AST/Type.cpp
+++ b/lib/AST/Type.cpp
@@ -75,8 +75,13 @@ const Type *Type::getArrayElementTypeNoTypeQual() const {
/// to getting the canonical type, but it doesn't remove *all* typedefs. For
/// example, it returns "T*" as "T*", (not as "int*"), because the pointer is
/// concrete.
-QualType QualType::getDesugaredType() const {
- return getTypePtr()->getDesugaredType()
+///
+/// \param ForDisplay When true, the desugaring is provided for
+/// display purposes only. In this case, we apply more heuristics to
+/// decide whether it is worth providing a desugared form of the type
+/// or not.
+QualType QualType::getDesugaredType(bool ForDisplay) const {
+ return getTypePtr()->getDesugaredType(ForDisplay)
.getWithAdditionalQualifiers(getCVRQualifiers());
}
@@ -86,7 +91,12 @@ QualType QualType::getDesugaredType() const {
/// to getting the canonical type, but it doesn't remove *all* typedefs. For
/// example, it return "T*" as "T*", (not as "int*"), because the pointer is
/// concrete.
-QualType Type::getDesugaredType() const {
+///
+/// \param ForDisplay When true, the desugaring is provided for
+/// display purposes only. In this case, we apply more heuristics to
+/// decide whether it is worth providing a desugared form of the type
+/// or not.
+QualType Type::getDesugaredType(bool ForDisplay) const {
if (const TypedefType *TDT = dyn_cast(this))
return TDT->LookThroughTypedefs().getDesugaredType();
if (const TypeOfExprType *TOE = dyn_cast(this))
@@ -95,16 +105,26 @@ QualType Type::getDesugaredType() const {
return TOT->getUnderlyingType().getDesugaredType();
if (const TemplateSpecializationType *Spec
= dyn_cast(this)) {
+ if (ForDisplay)
+ return QualType(this, 0);
+
QualType Canon = Spec->getCanonicalTypeInternal();
if (Canon->getAsTemplateSpecializationType())
return QualType(this, 0);
return Canon->getDesugaredType();
}
- if (const QualifiedNameType *QualName = dyn_cast(this))
- return QualName->getNamedType().getDesugaredType();
+ if (const QualifiedNameType *QualName = dyn_cast(this)) {
+ if (ForDisplay) {
+ // If desugaring the type that the qualified name is referring to
+ // produces something interesting, that's our desugared type.
+ QualType NamedType = QualName->getNamedType().getDesugaredType();
+ if (NamedType != QualName->getNamedType())
+ return NamedType;
+ } else
+ return QualName->getNamedType().getDesugaredType();
+ }
- // FIXME: remove this cast.
- return QualType(const_cast(this), 0);
+ return QualType(this, 0);
}
/// isVoidType - Helper method to determine if this is the 'void' type.
diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp
index 7e72a8453b..f11ce2043a 100644
--- a/lib/Sema/Sema.cpp
+++ b/lib/Sema/Sema.cpp
@@ -40,7 +40,7 @@ static void ConvertArgToStringFn(Diagnostic::ArgumentKind Kind, intptr_t Val,
// If this is a sugared type (like a typedef, typeof, etc), then unwrap one
// level of the sugar so that the type is more obvious to the user.
- QualType DesugaredTy = Ty->getDesugaredType();
+ QualType DesugaredTy = Ty->getDesugaredType(true);
DesugaredTy.setCVRQualifiers(DesugaredTy.getCVRQualifiers() |
Ty.getCVRQualifiers());
diff --git a/test/SemaCXX/qualified-names-diag.cpp b/test/SemaCXX/qualified-names-diag.cpp
index a1591d01d4..3bffd7c05d 100644
--- a/test/SemaCXX/qualified-names-diag.cpp
+++ b/test/SemaCXX/qualified-names-diag.cpp
@@ -21,12 +21,12 @@ namespace bar {
void test() {
foo::wibble::x a;
::bar::y b;
- a + b; // expected-error{{invalid operands to binary expression ('foo::wibble::x' (aka 'struct foo::wibble::x') and '::bar::y' (aka 'int'))}}
+ a + b; // expected-error{{invalid operands to binary expression ('foo::wibble::x' and '::bar::y' (aka 'int'))}}
::foo::wibble::bar::wonka::x::y c;
- c + b; // expected-error{{invalid operands to binary expression ('::foo::wibble::bar::wonka::x::y' (aka 'struct foo::wibble::bar::wonka::x::y') and '::bar::y' (aka 'int'))}}
+ c + b; // expected-error{{invalid operands to binary expression ('::foo::wibble::bar::wonka::x::y' and '::bar::y' (aka 'int'))}}
- (void)sizeof(bar::incomplete); // expected-error{{invalid application of 'sizeof' to an incomplete type 'bar::incomplete' (aka 'struct bar::incomplete')}}
+ (void)sizeof(bar::incomplete); // expected-error{{invalid application of 'sizeof' to an incomplete type 'bar::incomplete'}}
}
int ::foo::wibble::bar::wonka::x::y::* ptrmem;
diff --git a/test/SemaTemplate/qualified-names-diag.cpp b/test/SemaTemplate/qualified-names-diag.cpp
index bf4ae11876..4bc8bdfc9a 100644
--- a/test/SemaTemplate/qualified-names-diag.cpp
+++ b/test/SemaTemplate/qualified-names-diag.cpp
@@ -12,5 +12,5 @@ void test() {
std::vector v1;
vector v2;
- v1 = v2; // expected-error{{incompatible type assigning 'vector' (aka 'class std::vector'), expected 'std::vector' (aka 'class std::vector')}}
+ v1 = v2; // expected-error{{incompatible type assigning 'vector', expected 'std::vector'}}
}
diff --git a/www/diagnostics.html b/www/diagnostics.html
index 38c87728ae..4f68f58d52 100644
--- a/www/diagnostics.html
+++ b/www/diagnostics.html
@@ -156,6 +156,48 @@ is useful for the compiler to expose underlying details of a typedef:
If the user was somehow confused about how the system "pid_t" typedef is
defined, Clang helpfully displays it with "aka".
+In C++, type preservation includes retaining any qualification written into type names. For example, if we take a small snippet of code such as:
+
+
+
+namespace services {
+ struct WebService { };
+}
+namespace myapp {
+ namespace servers {
+ struct Server { };
+ }
+}
+
+using namespace myapp;
+void addHTTPService(servers::Server const &server, ::services::WebService const *http) {
+ server += http;
+}
+
+
+
+and then compile it, we see that Clang is both providing more accurate information and is retaining the types as written by the user (e.g., "servers::Server", "::services::WebService"):
+
+
+ $ g++-4.2 -fsyntax-only t.cpp
+ t.cpp:9: error: no match for 'operator+=' in 'server += http'
+ $ clang -fsyntax-only t.cpp
+ t.cpp:9:10: error: invalid operands to binary expression ('servers::Server const' and '::services::WebService const *')
+ server += http;
+ ~~~~~~ ^ ~~~~
+
+
+Naturally, type preservation extends to uses of templates, and Clang retains information about how a particular template specialization (like std::vector<Real>
) was spelled within the source code. For example:
+
+
+ $ g++-4.2 -fsyntax-only t.cpp
+ t.cpp:12: error: no match for 'operator=' in 'str = vec'
+ $ clang -fsyntax-only t.cpp
+ t.cpp:12:7: error: incompatible type assigning 'vector<Real>', expected 'std::string' (aka 'class std::basic_string<char>')
+ str = vec;
+ ^ ~~~
+
+
Fix-it Hints
simple example + template<> example
@@ -203,11 +245,6 @@ implements the "wwopen" class of APIs):
In practice, we've found that this is actually more useful in multiply nested
macros that in simple ones.
-C++ Fun Examples
-
-...
-
-