[PATCH] D37035: Implement __builtin_LINE() et. al. to support source location capture.

Eric Fiselier via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Wed Jun 27 02:21:53 PDT 2018


EricWF marked 10 inline comments as done.
EricWF added inline comments.


================
Comment at: include/clang/AST/SourceLocExprScope.h:39
+    //    static_assert(foo() == __LINE__);
+    return OldVal == nullptr || isa<CXXDefaultInitExpr>(DefaultExpr) ||
+           !OldVal->isInSameContext(EvalContext);
----------------
rsmith wrote:
> Do we want the `isa` check here? I'd think the same problem would occur for default initializers:
> 
> ```
> struct A {
>   int n = __builtin_LINE();
> };
> struct B {
>   A a = {};
> };
> B b = {}; // should give this context as the current one, not the context of the initializer inside struct B
> ```
Good catch. Fixed.


================
Comment at: include/clang/AST/SourceLocExprScope.h:44
+public:
+  SourceLocExprScopeBase(const Expr *DefaultExpr, SourceLocExprScopeBase **Dest,
+                         EvalContextType *EvalContext)
----------------
rsmith wrote:
> I think it'd be cleaner to add a
> 
> ```
> struct Current {
>   SourceLocExprScopeBase *Scope;
> };
> ```
> 
> maybe with members to get the location and context for a given `SourceLocExpr`, and to pass in a `SourceLocExprScopeBase::Current &` here rather than a `SourceLocExprScopeBase **`.
Done. Hopefully I understood the comment.


================
Comment at: include/clang/Sema/Sema.h:4463
+
+  /// \brief build a potentially resolved SourceLocExpr.
+  ///
----------------
rsmith wrote:
> Capitalize "build" here.
Done and removed `\brief`.


================
Comment at: lib/AST/Expr.cpp:1924-1931
+  auto CreateString = [&](StringRef SVal) {
+    QualType StrTy = BuildSourceLocExprType(Ctx, getIdentType(),
+                                            /*IsArray*/ true, SVal.size() + 1);
+    StringLiteral *Lit = StringLiteral::Create(Ctx, SVal, StringLiteral::Ascii,
+                                               /*Pascal*/ false, StrTy, Loc);
+    assert(Lit && "should not be null");
+    return Lit;
----------------
rsmith wrote:
> It doesn't seem reasonable to create a new `StringLiteral` each time a `SourceLocExpr` is (constant-)evaluated, and seems like it would be unsound in some cases (we require that reevaluation produces the same result).
> 
> I think the `StringLiteral` here is just for convenience, right? (Instead we could model the `SourceLocExpr` as being its own kind of array lvalue for the purpose of constant expression evaluation, like we do for `ObjCEncodeExpr` for instance.)
OK. I think I have a solution for this. Please forgive my poor explanation of the problem. The problem was two fold:

First, the type of `SourceLocExpr` represents the decayed type of the underlying array, and not the array type or value-category itself (as `StringLiteral` and `ObjCEncodeExpr` do). Therefore it takes additional work to propagate and ensure that the existing machinery uses the "real array type" of the `SourceLocExpr` and not the type of the `SourceLocExpr` itself.

Second,,we need to determine, store, and propagate  the type,, value category, and value of a `SourceLocExpr` corresponding to the context in which we first encounter the  `SourceLocExpr. None of this contextual information is available in the AST after the fact. A lazy model where `SourceLocExpr` pretends to be a placeholder for the "real array type and value" until said subelements are actually accessed will produce different results than if the `SourceLocExpr` was correctly evaluated in the context it was initially encountered.

For Example:
```
const char *cur_file(const char *f = __builtin_FILE()) { return f; }
struct TestInit {
#line 100 "initial_scope.cpp"
  const char *my_file = cur_file();
  TestInit() {} // FILE should point here. We need to transform/determine the value and type of the `SourceLocExpr` in this context.
};
#line 200 "new_scope.cpp"
// If we delay evaluating `__builtin_FILE()` until this point, where it's actually needed, it will return `new_scope.cpp`,
// because we've already exited the `CXXDefaultInitExpr` scope created when first evaluating `TestInit() = default`. 
TestInit Default;
static_assert(strcmp(Default.my_file, "initial_scope.cpp") == 0); // Fails if lazily evaluated!
```

I worked around this problem by storing the "real array type", the used location, and the used context of  the `__builtin_FOO())` expression in the `LValueBase` produced when we first consider the `__builtin_FOO()` expression. 


================
Comment at: lib/AST/ExprConstant.cpp:698-704
+    /// \brief Source location information about the default argument expression
+    /// we're evaluating, if any.
+    SourceLocExprScope *CurCXXDefaultArgScope = nullptr;
+
+    /// \brief Source location information about the default member initializer
+    /// we're evaluating, if any.
+    SourceLocExprScope *CurCXXDefaultInitScope = nullptr;
----------------
rsmith wrote:
> Do we really need both of these? It would seem like one 'current' scope would be sufficient to me.
Indeed, we don't seem to need both so I've removed one.


================
Comment at: lib/CodeGen/CGExprScalar.cpp:595
+    } else {
+      StringLiteral *Str = SLE->getStringValue(Ctx, Loc, DC);
+      return CGF.EmitArrayToPointerDecay(Str).getPointer();
----------------
rsmith wrote:
> Should we make some attempt to reuse the same string literal for multiple source locations denoting the same file?
I think I've addressed this. Instead of creating a `StringLiteral` expressions to evaluate, the new code uses `GetAddrOfConstentCString` to create the constant address corresponding to the string value specified by the `SourceLocExpr`.


https://reviews.llvm.org/D37035





More information about the cfe-commits mailing list