[cfe-dev] [RFC] Improving import failure checking strategy inside ASTImporter

Alexey Sidorin via cfe-dev cfe-dev at lists.llvm.org
Thu May 17 17:02:09 PDT 2018


Some import helpers can make our life a bit easier. Some examples below:

     template <typename DeclTy>
     LLVM_NODISCARD bool importInto(DeclTy *&ToD, DeclTy *FromD) {
       if (auto Res = Importer.Import(FromD)) {
         ToD = cast_or_null<DeclTy>(*Res);
         return false;
       }
       return true;
     }

     template <typename DeclTy>
     LLVM_NODISCARD bool importIntoNonNull(DeclTy *&ToD, DeclTy *FromD) {
       if (auto Res = Importer.Import(FromD)) {
         ToD = cast<DeclTy>(*Res);
         return false;
       }
       return true;
     }

     template <typename DeclTy> Optional<DeclTy *> importCasted(DeclTy 
*FromD) {
       if (auto Res = Importer.Import(FromD))
         return cast_or_null<DeclTy>(*Res);
       return None;
     }

     template <typename DeclTy>
     Optional<DeclTy *> importCastedNonNull(DeclTy *FromD) {
       if (auto Res = Importer.Import(FromD))
         return cast<DeclTy>(*Res);
       return None;
     }

They can be used like this:

QualType ASTNodeImporter::VisitTypedefType(const TypedefType *T) {
   if (auto ToDecl = importCastedNonNull(T->getDecl()))
     return Importer.getToContext().getTypeDeclType(*ToDecl);
   return {};
}

...


   if (importInto(ToEPI.ExceptionSpec.SourceDecl,
                  FromEPI.ExceptionSpec.SourceDecl))
     return {};

   if (importInto(ToEPI.ExceptionSpec.SourceTemplate,
                  FromEPI.ExceptionSpec.SourceTemplate))
     return {};

...


QualType ASTNodeImporter::VisitUnresolvedUsingType(
     const UnresolvedUsingType *T) {
   UnresolvedUsingTypenameDecl *ToD, *ToPrevD, *FromD = T->getDecl();
   if (importInto(ToD, FromD) || importInto(ToPrevD, 
FromD->getPreviousDecl()))
     return {};

   return Importer.getToContext().getTypeDeclType(ToD, ToPrevD);
}

Unfortunately, LLVM_NODISCARD is only enabled for C++14 which is not our 
case.



02.05.2018 01:28, George Karpenkov пишет:
>
>
>> On May 1, 2018, at 3:22 PM, Alexey Sidorin <alexey.v.sidorin at ya.ru 
>> <mailto:alexey.v.sidorin at ya.ru>> wrote:
>>
>> Hi George,
>>
>> You have wrote exactly the first idea I have described in the initial 
>> proposal:
>> > The problem with enforcing checks is the most annoying one but also 
>> looks to be the simplest - we can just replace the return value with 
>> Optional or ErrorOr so clients will be enforced to perform a failure 
>> checking.
>>
>> My question was about avoiding related boilerplate with nested if's 
>> (or lots of early returns). Optionals are excellent if one needs to 
>> do in-place checks. But if we have >50 nested Import() calls (which 
>> is a pretty common case for recursive ASTImporter), and we want to 
>> check only the top-level result, their usage becomes questionable. 
>> However, it still looks to be the best option we have. Adding an 
>> argument passed by reference in addition to Optional doesn't look 
>> good to me as well; moreover, we have Expected/ErrorOr for these 
>> usage scenarios.
>>
>> Anyway, maybe even my question itself is not correct. The question of 
>> error handling doesn't exist on its own, it is the result of 
>> ASTImporter design. Maybe a potential solution is to split the lookup 
>> and structural equivalence checking (to a kind of dry run) from node 
>> import: in this case, Import() is called only after all checks are 
>> done so it cannot fail. But this will need ASTImporter redesign, and 
>> it is definitely not a thing I'm going to work on soon.
>
> Right, I see, sorry for misunderstanding.
> The advantage of using “error” passed by reference is that it could be 
> reused across multiple `.import` calls;
> having 50+ nested import calls could be mitigated by wrapping related 
> groups in functions,
> that still seems to me a lesser evil than macros.
>
>>
>> 01.05.2018 23:43, George Karpenkov via cfe-dev пишет:
>>> Hi Aleksei,
>>>>>>> The problem of improving readability of these checks is a bit harder. The
>>>>>>> most straightforward approach is to create a macro like:
>>>>>>>
>>>>>>> #define IMPORT_PTR_INTO_VAR(VarName, Type, From) \
>>>>>>>    Type *VarName = nullptr;                                 \
>>>>>>>    {                                                        \
>>>>>>>      auto MacroImportRes = Importer.Import(From);           \
>>>>>>>      if (!MacroImportRes)                                   \
>>>>>>>        return MacroImportRes.getError();                    \
>>>>>>>      VarName = *MacroImportRes;                             \
>>>>>>>    }
>>>>>>>
>>>>>>> Same for non-pointer type. So, the final code will look like this:
>>>>>>>
>>>>>>> ErrorOr<Expr *> ASTNodeImporter::VisitCXXMemberCallExpr(CXXMemberCallExpr
>>>>>>> *E) {
>>>>>>>    IMPORT_QUAL_TYPE_INTO_VAR(T, E->getType())
>>>>>>>    IMPORT_PTR_INTO_VAR(ToFn, Expr, E->getCallee())
>>>>>>>
>>>>>>>    SmallVector<Expr *, 4> ToArgs(E->getNumArgs());
>>>>>>>    IMPORT_CONTAINER(E->arguments(), ToArgs);
>>>>>>>
>>>>>>>    return new (Importer.getToContext()) CXXMemberCallExpr(
>>>>>>>          Importer.getToContext(), ToFn, ToArgs, T, E->getValueKind(),
>>>>>>>          Importer.Import(E->getRParenLoc()));
>>>>>>> }
>>>>>>>
>>>>>>> But macros don't look clean. Unfortunately, I still haven't found a better
>>>>>>> option so any suggestions are very welcome.
>>>
>>> Macros are indeed ugly, and exceptions are even worse.
>>> I think a better solution would be to return an Optional, and make 
>>> `Importer.Import` accept an out-parameter `Error`, passed by reference.
>>> Then you could write:
>>>
>>> Optional<Expr*> VisitCXXMemberCallExpr(CXXMemberCallExpr *E) {
>>>   Error e;
>>>   if (auto T = Importer.import(E->getType(), e))
>>>     if (auto ToFn = Importer.import(E->getCallee(), e))
>>>       if (auto ToArgs = Importer.import(E->arguments(), e)
>>>         return new (Importer.getToContext()) CXXMemberCallExpr(…)
>>>   outE = e;
>>>   return none;
>>> }
>>>
>>> That’s even more concise than your example, explicit and does not 
>>> need macros.
>>>
>>> This would be less verbose in C++17 where you would not need nested 
>>> if’s,
>>> but sadly we don’t have that in LLVM yet.
>>>
>>> George
>>>
>>>
>>> _______________________________________________
>>> cfe-dev mailing list
>>> cfe-dev at lists.llvm.org
>>> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
>>
>>
>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20180518/e95ab6bf/attachment.html>


More information about the cfe-dev mailing list