From: Douglas Gregor Date: Wed, 1 Apr 2009 15:47:24 +0000 (+0000) Subject: Give Type::getDesugaredType a "for-display" mode that can apply more X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=969c689d893a248eca4f049f5b89f747e66e4bff;p=clang 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 --- 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

- -

...

- -