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<char>" down to their underlying "class basic_string<char, char_traits<char>, allocator<char>>, 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. llvm-svn: 68207
This commit is contained in:
parent
d28577651e
commit
2e0757f319
|
@ -162,7 +162,7 @@ public:
|
||||||
/// to getting the canonical type, but it doesn't remove *all* typedefs. For
|
/// 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
|
/// example, it returns "T*" as "T*", (not as "int*"), because the pointer is
|
||||||
/// concrete.
|
/// concrete.
|
||||||
QualType getDesugaredType() const;
|
QualType getDesugaredType(bool ForDisplay = false) const;
|
||||||
|
|
||||||
/// operator==/!= - Indicate whether the specified types and qualifiers are
|
/// operator==/!= - Indicate whether the specified types and qualifiers are
|
||||||
/// identical.
|
/// identical.
|
||||||
|
@ -461,7 +461,7 @@ public:
|
||||||
/// to getting the canonical type, but it doesn't remove *all* typedefs. For
|
/// 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
|
/// example, it returns "T*" as "T*", (not as "int*"), because the pointer is
|
||||||
/// concrete.
|
/// concrete.
|
||||||
QualType getDesugaredType() const;
|
QualType getDesugaredType(bool ForDisplay = false) const;
|
||||||
|
|
||||||
/// More type predicates useful for type checking/promotion
|
/// More type predicates useful for type checking/promotion
|
||||||
bool isPromotableIntegerType() const; // C99 6.3.1.1p2
|
bool isPromotableIntegerType() const; // C99 6.3.1.1p2
|
||||||
|
|
|
@ -75,8 +75,13 @@ const Type *Type::getArrayElementTypeNoTypeQual() const {
|
||||||
/// to getting the canonical type, but it doesn't remove *all* typedefs. For
|
/// 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
|
/// example, it returns "T*" as "T*", (not as "int*"), because the pointer is
|
||||||
/// concrete.
|
/// 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());
|
.getWithAdditionalQualifiers(getCVRQualifiers());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +91,12 @@ QualType QualType::getDesugaredType() const {
|
||||||
/// to getting the canonical type, but it doesn't remove *all* typedefs. For
|
/// 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
|
/// example, it return "T*" as "T*", (not as "int*"), because the pointer is
|
||||||
/// concrete.
|
/// 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<TypedefType>(this))
|
if (const TypedefType *TDT = dyn_cast<TypedefType>(this))
|
||||||
return TDT->LookThroughTypedefs().getDesugaredType();
|
return TDT->LookThroughTypedefs().getDesugaredType();
|
||||||
if (const TypeOfExprType *TOE = dyn_cast<TypeOfExprType>(this))
|
if (const TypeOfExprType *TOE = dyn_cast<TypeOfExprType>(this))
|
||||||
|
@ -95,16 +105,26 @@ QualType Type::getDesugaredType() const {
|
||||||
return TOT->getUnderlyingType().getDesugaredType();
|
return TOT->getUnderlyingType().getDesugaredType();
|
||||||
if (const TemplateSpecializationType *Spec
|
if (const TemplateSpecializationType *Spec
|
||||||
= dyn_cast<TemplateSpecializationType>(this)) {
|
= dyn_cast<TemplateSpecializationType>(this)) {
|
||||||
|
if (ForDisplay)
|
||||||
|
return QualType(this, 0);
|
||||||
|
|
||||||
QualType Canon = Spec->getCanonicalTypeInternal();
|
QualType Canon = Spec->getCanonicalTypeInternal();
|
||||||
if (Canon->getAsTemplateSpecializationType())
|
if (Canon->getAsTemplateSpecializationType())
|
||||||
return QualType(this, 0);
|
return QualType(this, 0);
|
||||||
return Canon->getDesugaredType();
|
return Canon->getDesugaredType();
|
||||||
}
|
}
|
||||||
if (const QualifiedNameType *QualName = dyn_cast<QualifiedNameType>(this))
|
if (const QualifiedNameType *QualName = dyn_cast<QualifiedNameType>(this)) {
|
||||||
return QualName->getNamedType().getDesugaredType();
|
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(this, 0);
|
||||||
return QualType(const_cast<Type*>(this), 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// isVoidType - Helper method to determine if this is the 'void' type.
|
/// isVoidType - Helper method to determine if this is the 'void' type.
|
||||||
|
|
|
@ -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
|
// 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.
|
// 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() |
|
DesugaredTy.setCVRQualifiers(DesugaredTy.getCVRQualifiers() |
|
||||||
Ty.getCVRQualifiers());
|
Ty.getCVRQualifiers());
|
||||||
|
|
||||||
|
|
|
@ -21,12 +21,12 @@ namespace bar {
|
||||||
void test() {
|
void test() {
|
||||||
foo::wibble::x a;
|
foo::wibble::x a;
|
||||||
::bar::y b;
|
::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;
|
::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;
|
int ::foo::wibble::bar::wonka::x::y::* ptrmem;
|
||||||
|
|
|
@ -12,5 +12,5 @@ void test() {
|
||||||
|
|
||||||
std::vector<INT> v1;
|
std::vector<INT> v1;
|
||||||
vector<Real> v2;
|
vector<Real> v2;
|
||||||
v1 = v2; // expected-error{{incompatible type assigning 'vector<Real>' (aka 'class std::vector<float>'), expected 'std::vector<INT>' (aka 'class std::vector<int>')}}
|
v1 = v2; // expected-error{{incompatible type assigning 'vector<Real>', expected 'std::vector<INT>'}}
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,6 +156,48 @@ is useful for the compiler to expose underlying details of a typedef:</p>
|
||||||
<p>If the user was somehow confused about how the system "pid_t" typedef is
|
<p>If the user was somehow confused about how the system "pid_t" typedef is
|
||||||
defined, Clang helpfully displays it with "aka".</p>
|
defined, Clang helpfully displays it with "aka".</p>
|
||||||
|
|
||||||
|
<p>In C++, type preservation includes retaining any qualification written into type names. For example, if we take a small snippet of code such as:
|
||||||
|
|
||||||
|
<blockquote>
|
||||||
|
<pre>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
</blockquote>
|
||||||
|
|
||||||
|
<p>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"):
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
$ <b>g++-4.2 -fsyntax-only t.cpp</b>
|
||||||
|
t.cpp:9: error: no match for 'operator+=' in 'server += http'
|
||||||
|
$ <b>clang -fsyntax-only t.cpp</b>
|
||||||
|
t.cpp:9:10: error: invalid operands to binary expression ('servers::Server const' and '::services::WebService const *')
|
||||||
|
<font color="darkgreen">server += http;</font>
|
||||||
|
<font color="blue">~~~~~~ ^ ~~~~</font>
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p>Naturally, type preservation extends to uses of templates, and Clang retains information about how a particular template specialization (like <code>std::vector<Real></code>) was spelled within the source code. For example:</p>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
$ <b>g++-4.2 -fsyntax-only t.cpp</b>
|
||||||
|
t.cpp:12: error: no match for 'operator=' in 'str = vec'
|
||||||
|
$ <b>clang -fsyntax-only t.cpp</b>
|
||||||
|
t.cpp:12:7: error: incompatible type assigning 'vector<Real>', expected 'std::string' (aka 'class std::basic_string<char>')
|
||||||
|
<font color="darkgreen">str = vec</font>;
|
||||||
|
<font color="blue">^ ~~~</font>
|
||||||
|
</pre>
|
||||||
|
|
||||||
<h2>Fix-it Hints</h2>
|
<h2>Fix-it Hints</h2>
|
||||||
|
|
||||||
<p>simple example + template<> example</p>
|
<p>simple example + template<> example</p>
|
||||||
|
@ -203,11 +245,6 @@ implements the "wwopen" class of APIs):</p>
|
||||||
<p>In practice, we've found that this is actually more useful in multiply nested
|
<p>In practice, we've found that this is actually more useful in multiply nested
|
||||||
macros that in simple ones.</p>
|
macros that in simple ones.</p>
|
||||||
|
|
||||||
<h2>C++ Fun Examples</h2>
|
|
||||||
|
|
||||||
<p>...</p>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Reference in New Issue