With regard to what #2 is doing, I threw the following into a file with
nsRefPtr, and got clang to dump the AST:

struct Foo {void AddRef() {} void Release() {}};
nsRefPtr<Foo> bar;
void foo() {
  Foo* x = true ? nullptr : bar;
}

`-FunctionDecl 0x103805750 <line:943:1, line:945:1> line:943:6 foo 'void
(void)'
  `-CompoundStmt 0x103807708 <col:12, line:945:1>
    `-DeclStmt 0x1038076f0 <line:944:3, col:32>
      `-VarDecl 0x103805800 <col:3, col:29> col:8 x 'struct Foo *' cinit
        `-ExprWithCleanups 0x1038076d8 <col:12, col:29> 'struct Foo *'
          `-ImplicitCastExpr 0x1038076c0 <col:12, col:29> 'struct Foo *'
<UserDefinedConversion>
            `-CXXMemberCallExpr 0x103807698 <col:12, col:29> 'struct Foo *'
              `-MemberExpr 0x103807668 <col:12, col:29> '<bound member
function type>' .operator Foo * 0x1038048a0
                `-ImplicitCastExpr 0x103807650 <col:12, col:29> 'const
class nsRefPtr<struct Foo>' <NoOp>
                  `-ConditionalOperator 0x103807618 <col:12, col:29>
'nsRefPtr<struct Foo>':'class nsRefPtr<struct Foo>'
                    |-CXXBoolLiteralExpr 0x103805858 <col:12> '_Bool' true
                    |-CXXBindTemporaryExpr 0x1038074a8 <col:19>
'nsRefPtr<struct Foo>':'class nsRefPtr<struct Foo>' (CXXTemporary
0x1038074a0)
                    | `-CXXConstructExpr 0x103807468 <col:19>
'nsRefPtr<struct Foo>':'class nsRefPtr<struct Foo>' 'void (nsRefPtr<struct
Foo> &&)' elidable
                    |   `-MaterializeTemporaryExpr 0x103807450 <col:19>
'nsRefPtr<struct Foo>':'class nsRefPtr<struct Foo>' xvalue
                    |     `-CXXBindTemporaryExpr 0x103807358 <col:19>
'nsRefPtr<struct Foo>':'class nsRefPtr<struct Foo>' (CXXTemporary
0x103807350)
                    |       `-CXXConstructExpr 0x103807318 <col:19>
'nsRefPtr<struct Foo>':'class nsRefPtr<struct Foo>' 'void (nsRefPtr<struct
Foo> &&)' elidable
                    |         `-MaterializeTemporaryExpr 0x103807300
<col:19> 'nsRefPtr<struct Foo>':'class nsRefPtr<struct Foo>' xvalue
                    |           `-CXXBindTemporaryExpr 0x103806f38 <col:19>
'nsRefPtr<struct Foo>':'class nsRefPtr<struct Foo>' (CXXTemporary
0x103806f30)
                    |             `-ImplicitCastExpr 0x103806f10 <col:19>
'nsRefPtr<struct Foo>':'class nsRefPtr<struct Foo>' <ConstructorConversion>
                    |               `-CXXConstructExpr 0x103806ed8 <col:19>
'nsRefPtr<struct Foo>':'class nsRefPtr<struct Foo>' 'void (struct Foo *)'
                    |                 `-ImplicitCastExpr 0x103806ec0
<col:19> 'struct Foo *' <NullToPointer>
                    |                   `-CXXNullPtrLiteralExpr 0x103805870
<col:19> 'nullptr_t'
                    `-CXXBindTemporaryExpr 0x1038075f8 <col:29>
'nsRefPtr<struct Foo>':'class nsRefPtr<struct Foo>' (CXXTemporary
0x1038075f0)
                      `-CXXConstructExpr 0x1038075b8 <col:29>
'nsRefPtr<struct Foo>':'class nsRefPtr<struct Foo>' 'void (const
nsRefPtr<struct Foo> &)'
                        `-ImplicitCastExpr 0x1038075a0 <col:29> 'const
nsRefPtr<struct Foo>':'const class nsRefPtr<struct Foo>' lvalue <NoOp>
                          `-DeclRefExpr 0x103805888 <col:29>
'nsRefPtr<struct Foo>':'class nsRefPtr<struct Foo>' lvalue Var 0x103084da0
'bar' 'nsRefPtr<struct Foo>':'class nsRefPtr<struct Foo>'

In summary, the nsRefPtr is copied into a temporary in it's side of the
conditional. the nullptr is cast to a struct Foo*, which is constructed
into a nsRefPtr, which is bound to a temporary, and then moved around a bit
between temporaries. The resulting temporary xvalue then has the raw
pointer extracted from it (which uses the rvalue-reference cast call - the
part which is causing the problem), which is stored locally after the
temporaries are destroyed. The temporaries which are created in the
conditional are then destroyed, which causes a Release(), but that is OK
because there was an AddRef due to the extra copy in the nsRefPtr branch.

So the ternary actually causes an unnecessary AddRef/Release pair, neat.
The problem here appears to be that when deciding the type of the ternary,
c++ chooses nsRefPtr<Foo>, rather than Foo*. Adding the get() makes C++
choose the correct type for the ternary, and avoids the cast of the rvalue
reference.


On Wed, Jul 1, 2015 at 3:48 PM, Nathan Froyd <nfr...@mozilla.com> wrote:

> On Wed, Jul 1, 2015 at 7:39 AM, Aryeh Gregor <a...@aryeh.name> wrote:
>
> > On Wed, Jul 1, 2015 at 6:24 AM, Joshua Cranmer [image: 🐧] <
> pidgeo...@gmail.com>
> > wrote:
> > > You'd get the same benefit, I think, by making operator T*() && =
> > delete;, syntax which is accepted on gcc 4.8.1+, clang, and MSVC 2015
> IIRC.
> >
> > I once tried this and found it had problematic side effects that made
> > deploying it difficult in practice.  I think one involved the ternary
> > operator.  Unfortunately, I can't remember exactly what they were.  :(
> >  Anyone who's interested can try for themselves by just adding that
> > line to nsCOMPtr.h, recompiling, and seeing what breaks.
> >
>
> I tried this, fixed a few compilation errors, then decided this wasn't
> worth it just yet and threw my work into a bug.  Comments appreciated:
>
> https://bugzilla.mozilla.org/show_bug.cgi?id=1179451
>
> -Nathan
> _______________________________________________
> dev-platform mailing list
> dev-platform@lists.mozilla.org
> https://lists.mozilla.org/listinfo/dev-platform
>
_______________________________________________
dev-platform mailing list
dev-platform@lists.mozilla.org
https://lists.mozilla.org/listinfo/dev-platform

Reply via email to