[cfe-commits] r58509 - in /cfe/trunk: include/clang/Basic/DiagnosticKinds.def lib/AST/Expr.cpp lib/Sema/Sema.h lib/Sema/SemaExprCXX.cpp lib/Sema/SemaInherit.cpp lib/Sema/SemaInherit.h lib/Sema/SemaOverload.cpp test/SemaCXX/condition.cpp test/SemaCXX/dynamic-cast.cpp test/SemaCXX/static-cast.cpp
Chris Lattner
clattner at apple.com
Mon Nov 3 08:29:54 PST 2008
On Oct 31, 2008, at 7:43 AM, Sebastian Redl wrote:
> URL: http://llvm.org/viewvc/llvm-project?rev=58509&view=rev
> Log:
> Implement semantic checking of static_cast and dynamic_cast.
Very nice Sebastian, thank you for your contribution!
Some thoughts below. I'm leaving the "deep" issues to Doug, but I'll
nit pick some details :)
> +++ cfe/trunk/include/clang/Basic/DiagnosticKinds.def Fri Oct 31
> 09:43:28 2008
> @@ -1096,6 +1096,8 @@
> "an extension")
> DIAG(err_bad_reinterpret_cast_small_int, ERROR,
> "cast from pointer to smaller type '%0' loses information")
> +DIAG(err_bad_dynamic_cast_operand, ERROR,
> + "'%0' is %1")
This is really dangerous. I note that you use this like this:
> + Diag(OpLoc, diag::err_bad_dynamic_cast_operand,
> + OrigDestType.getAsString(), "not a reference or pointer",
> DestRange);
> + Diag(OpLoc, diag::err_bad_dynamic_cast_operand,
> + DestPointee.getUnqualifiedType().getAsString(),
> + "incomplete", DestRange);
> + Diag(OpLoc, diag::err_bad_dynamic_cast_operand,
> + DestPointee.getUnqualifiedType().getAsString(),
> + "not a class", DestRange);
etc. The problem with this is that we'd like to keep diagnostics
localizable. In practice, this means that the only thing that can be
substituted into a "%" variable safely are 1) standard language
keywords 2) things that are reprinted from the user's source code
(e.g. types).
Substituting in random text won't work, because we lose the ability to
just localize the diagnostic catalog. Please split these into
separate diagnostics for each case.
> =
> =
> =
> =
> =
> =
> =
> =
> ======================================================================
> --- cfe/trunk/lib/AST/Expr.cpp (original)
> +++ cfe/trunk/lib/AST/Expr.cpp Fri Oct 31 09:43:28 2008
> @@ -1072,15 +1072,17 @@
> /// integer constant expression with the value zero, or if this is
> one that is
> /// cast to void*.
> bool Expr::isNullPointerConstant(ASTContext &Ctx) const {
> - // Strip off a cast to void*, if it exists.
> + // Strip off a cast to void*, if it exists. Except in C++.
> if (const ExplicitCastExpr *CE = dyn_cast<ExplicitCastExpr>(this)) {
> + if(!Ctx.getLangOptions().CPlusPlus) {
It's a minor detail, but please add a space after the if. The
prevailing heuristic is that we put a space between a keyword and open
paren, but not a space between an identifier and open paren (as in
function calls).
> +++ cfe/trunk/lib/Sema/Sema.h Fri Oct 31 09:43:28 2008
> @@ -707,13 +707,22 @@
> SourceLocation LParenLoc,
> ExprTy *E,
> SourceLocation RParenLoc);
>
> +private:
> // Helpers for ActOnCXXCasts
> - bool CastsAwayConstness(QualType SrcType, QualType DestType);
> void CheckConstCast(SourceLocation OpLoc, Expr *&SrcExpr, QualType
> DestType);
> void CheckReinterpretCast(SourceLocation OpLoc, Expr *&SrcExpr,
> QualType DestType);
> void CheckStaticCast(SourceLocation OpLoc, Expr *&SrcExpr,
> QualType DestType);
> + void CheckDynamicCast(SourceLocation OpLoc, Expr *&SrcExpr,
> + QualType DestType, const SourceRange
> &DestRange);
> + bool CastsAwayConstness(QualType SrcType, QualType DestType);
> + bool IsStaticReferenceDowncast(Expr *SrcExpr, QualType DestType);
> + bool IsStaticPointerDowncast(QualType SrcType, QualType DestType);
> + bool IsStaticDowncast(QualType SrcType, QualType DestType);
> + ImplicitConversionSequence TryDirectInitialization(Expr *SrcExpr,
> + QualType
> DestType);
Instead of these being methods on Sema, would it be reasonable to make
them static functions that take Sema as an argument? The benefit of
doing this is to reduce the number of methods on Sema. If it is
outrageously ugly to do this, please don't, but as a general principle
it is good to prefer static functions where possible.
> DestType = Context.getCanonicalType(DestType);
Does this code really need to strip down to canonical types? Your
code seems very clean and uses predicates like ->getAsFoo and -
>isFoo() consistently. If it is safe, it would be nice to just use
these predicates consistently and avoid canonicalizing them. The
major benefit of this is that it means you don't need an "orig" type
as well as the canonical one.
If you decide that you need to canonicalize, I'd prefer the code to
keep the original type named "DestType" (for example) and name the
canonical type "CanDestType" or "DestTypeC" or something like that, to
make it obvious to the reader that the type has been canonicalized.
> + // C++ 5.2.9p5, reference downcast.
> + // See the function for details.
> + if (IsStaticReferenceDowncast(SrcExpr, DestType)) {
> + return;
> + }
> +
> + // C++ 5.2.9p2: An expression e can be explicitly converted to a
> type T
> + // [...] if the declaration "T t(e);" is well-formed, [...].
> + ImplicitConversionSequence ICS = TryDirectInitialization(SrcExpr,
> DestType);
> + if (ICS.ConversionKind !=
> ImplicitConversionSequence::BadConversion) {
> + if (ICS.ConversionKind ==
> ImplicitConversionSequence::StandardConversion &&
> + ICS.Standard.First != ICK_Identity)
> + {
Sometimes you're putting open braces on their own line, sometimes on
the same line as the if (likewise for method bodies). I personally
don't care which, but consistency is good. Most of the code puts the
braces on the same line of the if/method, so it's probably best to go
with that.
> + if (SrcExpr->isLvalue(Context) != Expr::LV_Valid) {
> + return false;
> + }
Another minor issue: generally we avoid braces for really simple if
conditions like this. If you really prefer them, they are ok and I'm
fine with keeping them, but if they don't add value, I have a slight
preference to elide them.
> + DestType = Context.getCanonicalType(DestType);
> + const ReferenceType *DestReference = DestType-
> >getAsReferenceType();
> + if (!DestReference) {
> + return false;
> + }
> + QualType DestPointee = DestReference->getPointeeType();
> +
> + QualType SrcType = Context.getCanonicalType(SrcExpr->getType());
> +
> + return IsStaticDowncast(SrcType, DestPointee);
Instead of requiring all the callers to pass in canonical types, it
would be cleaner (from an interface perspective) to make
IsStaticDowncast canonicalize the type if it really needs to. This
makes the caller simpler and makes the function more general. It's
even better to just not require canonical types of course ;-)
> +/// IsStaticDowncast - Common functionality of
> IsStaticReferenceDowncast and
> +/// IsStaticPointerDowncast. Tests whether a static downcast from
> SrcType to
> +/// DestType, both of which must be canonical, is possible and
> allowed.
> +bool
> +Sema::IsStaticDowncast(QualType SrcType, QualType DestType)
> +{
> + assert(SrcType->isCanonical());
> + assert(DestType->isCanonical());
> +
> + if (!DestType->isRecordType()) {
> + return false;
> + }
> +
> + if (!SrcType->isRecordType()) {
> + return false;
> + }
This is another very personal issue, but I'd generally prefer to merge
these into the same if, and use "||". There should also be a small
comment explaining why you reject non record types.
> + // Comparing cv is cheaper, so do it first.
> + if (!DestType.isAtLeastAsQualifiedAs(SrcType)) {
> + return false;
> + }
In contrast with the two above, keeping this one separated out seems
to add value, because it gives you a nice place to put the comment.
> + // Assumptions to this point.
> + assert(DestPointer || DestReference);
> + assert(DestRecord || DestPointee->isVoidType());
> + assert(SrcRecord);
This is a self-evident/redundant comment :). It would be better to
enhance the assertions with a message explaining why these must hold,
e.g.:
assert(SrcRecord && "Non-records were rejected above")
or something like that. Short messages like this are very useful when
someone else is debugging a problem in your code, because it makes it
more obvious *why* you thought the condition should hold.
> bool Sema::IsIntegralPromotion(Expr *From, QualType FromType,
> QualType ToType)
> {
> const BuiltinType *To = ToType->getAsBuiltinType();
> + if (!To) {
> + return false;
> + }
This deserves a comment. Maybe something like "all integers are
builtin types"?
Thanks again for the contribution, this is excellent work!
-Chris
More information about the cfe-commits
mailing list