injecting a member function

For my work on MS and Borland properties, I need to be able transform
post-inc and post-dec expressions like "obj.prop++" into some equivalent
expression that uses getter and setter methods. (Prefix ops are simple
because they don't require the creation of a temporary to store the
result of the expression.)

I'm thinking of approaching the problem like this: "obj.prop++" is
replaced with "obj.__builtin_postinc_Obj_Prop()", where that member is
generated on-demand and injected in the Obj class. The member would
essentially be:

  PropTy Obj::__builtin_postinc_Obj_Prop() {
    PropTy tmp = this->getProp();
    this->setProp(tmp + 1);
    return tmp;
  }

Two questions:

1) Is this the correct approach? Or is there a simpler/easier way?
2) Is there any precedent in clang for this? I'm looking for code I can
crib from, or routines I could reuse.

Thanks in advance,

I think you want to make some sort of PropertyAccessOperator
expression. It would work basically like BinaryConditionalOperator
does, which is to say, it would bind the result of an expression
(the base of the property) to an OpaqueValueExpr, then perform
an arbitrary expression using that. For source fidelity, it would also
preserve the original member expression (and RHS, where applicable).

As a more concrete example, “obj.prop++” would look something like this:

(PropertyAccessOperator int

The original operand, for source fidelity

(MemberExpr int lvalue property
(DeclRefExpr “obj” Obj lvalue))

The opaque value expression

(OpaqueValueExpr 0xabcdef Obj lvalue)

The expression whose result the OpaqueValueExpr will be bound to

(DeclRefExpr “obj” Obj lvalue)

The expression to evaluate, expressed in terms of the OVE

(CXXMemberCallExpr void
(MemberExpr void(int) .setBase
(OpaqueValueExpr 0xabcdef Obj lvalue))
(CXXMemberCallExpr int
(MemberExpr PropTy() .getBase
(OpaqueValueExpr 0xabcdef Obj lvalue)))))

Hmm. Unfortunately, I’m not sure how to indicate what the result of the
operation should be.

John.

For my work on MS and Borland properties, I need to be able transform
post-inc and post-dec expressions like "obj.prop++" into some equivalent
expression that uses getter and setter methods. (Prefix ops are simple
because they don't require the creation of a temporary to store the
result of the expression.)

I'm thinking of approaching the problem like this: "obj.prop++" is
replaced with "obj.__builtin_postinc_Obj_Prop()", where that member is
generated on-demand and injected in the Obj class. The member would
essentially be:

PropTy Obj::__builtin_postinc_Obj_Prop() {
   PropTy tmp = this->getProp();
   this->setProp(tmp + 1);
   return tmp;
}

Two questions:

1) Is this the correct approach? Or is there a simpler/easier way?
2) Is there any precedent in clang for this? I'm looking for code I can
crib from, or routines I could reuse.

I think you want to make some sort of PropertyAccessOperator
expression. It would work basically like BinaryConditionalOperator

Thanks, John. Do you mean "ConditionalOperator" (which is ternary)?

does, which is to say, it would bind the result of an expression
(the base of the property) to an OpaqueValueExpr, then perform
an arbitrary expression using that. For source fidelity, it would also
preserve the original member expression (and RHS, where applicable).

<aside>
Regarding that, we abandoned our earlier plan to save original
expressions directly in the AST via a RewrittenExpr node. It broke to
much code that was explicitly testing nodes for their StmtClass.
Instead, the AST contains the rewritten expression, and the original
expression is stored in a map in the ASTContext, where it's available to
anybody who's interested. (The mapping is preserved by the AST
serialization code.)

For performance sake, we added a bit to Expr to note that a given node
was the result of a rewrite and that the original expression can be
found elsewhere.
<aside>

As a more concrete example, "obj.prop++" would look something like this:

  (PropertyAccessOperator int
    # The original operand, for source fidelity
    (MemberExpr int lvalue property
      (DeclRefExpr "obj" Obj lvalue))
    # The opaque value expression
    (OpaqueValueExpr 0xabcdef Obj lvalue)

I'm not 100% clear what the purpose of this OpaqueValueExpr is, but
maybe it will become clear when I study the code. Is it a temporary that
is storing the result of obj.getProp()? Shouldn't that be represented in
the AST somehow?

    # The expression whose result the OpaqueValueExpr will be bound to
    (DeclRefExpr "obj" Obj lvalue)
    # The expression to evaluate, expressed in terms of the OVE
    (CXXMemberCallExpr void
      (MemberExpr void(int) .setBase
        (OpaqueValueExpr 0xabcdef Obj lvalue))
      (CXXMemberCallExpr int
        (MemberExpr PropTy() .getBase
          (OpaqueValueExpr 0xabcdef Obj lvalue)))))

I don't yet have a mental model of what this AST represents. I'll go off
and study ConditionalOperator and maybe it'll become clear.

Hmm. Unfortunately, I'm not sure how to indicate what the result of the
operation should be.

Heh, that's kinda the problem I'm trying to solve. :stuck_out_tongue:

I think you want to make some sort of PropertyAccessOperator
expression. It would work basically like BinaryConditionalOperator

Thanks, John. Do you mean "ConditionalOperator" (which is ternary)?

No, I mean BinaryConditionalOperator, which represents the GNU
omitted-middle-operand ?: extension, and which has similar issues with
a sub-expression that's used in multiple places. This is new in ToT.

does, which is to say, it would bind the result of an expression
(the base of the property) to an OpaqueValueExpr, then perform
an arbitrary expression using that. For source fidelity, it would also
preserve the original member expression (and RHS, where applicable).

<aside>
Regarding that, we abandoned our earlier plan to save original
expressions directly in the AST via a RewrittenExpr node. It broke to
much code that was explicitly testing nodes for their StmtClass.
Instead, the AST contains the rewritten expression, and the original
expression is stored in a map in the ASTContext, where it's available to
anybody who's interested. (The mapping is preserved by the AST
serialization code.)

I don't see how this can work; the property base needs to be used in
multiple places in the rewritten expression.

As a more concrete example, "obj.prop++" would look something like this:

(PropertyAccessOperator int
   # The original operand, for source fidelity
   (MemberExpr int lvalue property
     (DeclRefExpr "obj" Obj lvalue))
   # The opaque value expression
   (OpaqueValueExpr 0xabcdef Obj lvalue)

I'm not 100% clear what the purpose of this OpaqueValueExpr is, but
maybe it will become clear when I study the code. Is it a temporary that
is storing the result of obj.getProp()? Shouldn't that be represented in
the AST somehow?

It is represented in the AST: it's an expression node. Certain other
expression nodes can bind it to the result of an expression, which can
then be legitimately used in multiple places. The overall effect is like
a "let" expression in a functional language, where the OVE acts as a
reference to the bound variable. In functional syntax, my proposal was:
  let tmp = obj in
  tmp.setProp(tmp.getProp() + 1)

The best solution for dealing with the result might actually be to
introduce a *second* OpaqueValueExpr. In functional terms, this
would be:
  let tmp1 = obj in
  let tmp2 = tmp1.getProp() + 1 in
  (tmp1.setProp(tmp2); tmp2)

John.

I think you want to make some sort of PropertyAccessOperator
expression. It would work basically like BinaryConditionalOperator

Thanks, John. Do you mean "ConditionalOperator" (which is ternary)?

No, I mean BinaryConditionalOperator, which represents the GNU
omitted-middle-operand ?: extension, and which has similar issues with
a sub-expression that's used in multiple places. This is new in ToT.

Ah. That's explains why I didn't see it. I'm on a fork that is a bit
behind your trunk, and we don't have it yet. Forgive me for being a bit
slow on the uptake.

does, which is to say, it would bind the result of an expression
(the base of the property) to an OpaqueValueExpr, then perform
an arbitrary expression using that. For source fidelity, it would also
preserve the original member expression (and RHS, where applicable).

<aside>
Regarding that, we abandoned our earlier plan to save original
expressions directly in the AST via a RewrittenExpr node. It broke to
much code that was explicitly testing nodes for their StmtClass.
Instead, the AST contains the rewritten expression, and the original
expression is stored in a map in the ASTContext, where it's available to
anybody who's interested. (The mapping is preserved by the AST
serialization code.)

I don't see how this can work; the property base needs to be used in
multiple places in the rewritten expression.

OK, I'm slowly starting to understand. We were doing a naive rewrite of
"++a.b" to "a.set(a.get()+1)". You're saying that the expression "a"
should not be evaluated more than once, and you're right. OK, back to
the drawing board. :slight_smile:

As a more concrete example, "obj.prop++" would look something like this:

(PropertyAccessOperator int
   # The original operand, for source fidelity
   (MemberExpr int lvalue property
     (DeclRefExpr "obj" Obj lvalue))
   # The opaque value expression
   (OpaqueValueExpr 0xabcdef Obj lvalue)

I'm not 100% clear what the purpose of this OpaqueValueExpr is, but
maybe it will become clear when I study the code. Is it a temporary that
is storing the result of obj.getProp()? Shouldn't that be represented in
the AST somehow?

It is represented in the AST: it's an expression node. Certain other
expression nodes can bind it to the result of an expression, which can
then be legitimately used in multiple places. The overall effect is like
a "let" expression in a functional language, where the OVE acts as a
reference to the bound variable. In functional syntax, my proposal was:
  let tmp = obj in
  tmp.setProp(tmp.getProp() + 1)

In practical terms, that means that, when evaluating tmp, evaluate obj
(once), and thereafter all other times tmp is evaluated, just use the
result already computed. And this Just Works wrt lvalues and rvalues.

The best solution for dealing with the result might actually be to
introduce a *second* OpaqueValueExpr. In functional terms, this
would be:
  let tmp1 = obj in
  let tmp2 = tmp1.getProp() + 1 in
  (tmp1.setProp(tmp2); tmp2)

<nod> I think I'm there, finally. Thanks.

I think you want to make some sort of PropertyAccessOperator
expression. It would work basically like BinaryConditionalOperator

Thanks, John. Do you mean "ConditionalOperator" (which is ternary)?

No, I mean BinaryConditionalOperator, which represents the GNU
omitted-middle-operand ?: extension, and which has similar issues with
a sub-expression that's used in multiple places. This is new in ToT.

Ah. That's explains why I didn't see it. I'm on a fork that is a bit
behind your trunk, and we don't have it yet. Forgive me for being a bit
slow on the uptake.

No worries; I figured you were on a branch and just wanted to point
you at the new hotness. :slight_smile:

It is represented in the AST: it's an expression node. Certain other
expression nodes can bind it to the result of an expression, which can
then be legitimately used in multiple places. The overall effect is like
a "let" expression in a functional language, where the OVE acts as a
reference to the bound variable. In functional syntax, my proposal was:
let tmp = obj in
tmp.setProp(tmp.getProp() + 1)

In practical terms, that means that, when evaluating tmp, evaluate obj
(once), and thereafter all other times tmp is evaluated, just use the
result already computed. And this Just Works wrt lvalues and rvalues.

Right. When creating the OVE, you give it a type, value kind, and
value object kind matching the expression it gets bound to.

John.

Here's a question. What if I create a OpaqueValueExpr, and I want it to
be an lvalue (so I can mutate it in place), but the expression it gets
bound to is actually an rvalue? Will that work? Or can I cast an rvalue
to an lvalue somehow and bind the OVE to that?

The reason I ask is because I want this:

  a.b += 42

to be rewritten as:

  let tmp = a
  in tmp.set(tmp.get() += 42)

tmp.get() will (probably) be an rvalue, but for feature parity with MS,
I need to use += instead of the (obvious and simpler) +.

let tmp = obj in
tmp.setProp(tmp.getProp() + 1)

In practical terms, that means that, when evaluating tmp, evaluate obj
(once), and thereafter all other times tmp is evaluated, just use the
result already computed. And this Just Works wrt lvalues and rvalues.

Right. When creating the OVE, you give it a type, value kind, and
value object kind matching the expression it gets bound to.

Here's a question. What if I create a OpaqueValueExpr, and I want it to
be an lvalue (so I can mutate it in place), but the expression it gets
bound to is actually an rvalue? Will that work? Or can I cast an rvalue
to an lvalue somehow and bind the OVE to that?

OVEs need to match their underlying values perfectly, including in value kind.

The reason I ask is because I want this:

a.b += 42

to be rewritten as:

let tmp = a
in tmp.set(tmp.get() += 42)

tmp.get() will (probably) be an rvalue, but for feature parity with MS,
I need to use += instead of the (obvious and simpler) +.

Does this really need to be "tmp.set(tmp.get() += 42)"? I would expect it
to just be "tmp.get() += 42" if tmp.get() happens to yield an lvalue.

Regardless, you need to be testing for this and building different ASTs
around it rather than trying to force invalid ASTs.

John.