-
Notifications
You must be signed in to change notification settings - Fork 10
Added check for scope in structural equivalence (experimental). #629
base: ctu-clang7
Are you sure you want to change the base?
Conversation
48493c8
to
abc67c7
Compare
@@ -338,6 +430,23 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, | |||
llvm_unreachable("Invalid template argument kind"); | |||
} | |||
|
|||
/// Determine whether two template argument lists are equivalent. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this hunk related?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I see you use it when comparing the contexts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function is used in IsEquivalentContext
.
@@ -1633,6 +1742,8 @@ unsigned StructuralEquivalenceContext::getApplicableDiagnostic( | |||
return diag::warn_odr_parameter_pack_non_pack; | |||
case diag::err_odr_non_type_parameter_type_inconsistent: | |||
return diag::warn_odr_non_type_parameter_type_inconsistent; | |||
default: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is probably not related too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to make a build warning go away (I have another one in ASTImporterTest.cpp, probably a new PR can be created to correct this).
if (auto *ND1 = dyn_cast<NamedDecl>(D1)) { | ||
auto *ND2 = dyn_cast<NamedDecl>(D2); | ||
if (!ND2) | ||
return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if here we would just get the two DeclContexts from D1 and D2 and we would compare them by either NamedDecl::getQualifiedNameAsString or by index::generateUSRForDecl?
The USR seems more precise and both seems to be slow.
About the slowness we could add yet another local cache to the StructuralEquivalenceContext class.
How is it different than what we had previously in #624 ?
This time we would compare only the containing scopes by string and not the Decls themselves.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I am saying here is just an alternative solution to consider, not necessarily better ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to compare only scope, not the decl name itself (that is done already). Probably this was the reason for some test failures when the qualified name was used.
lib/AST/ASTStructuralEquivalence.cpp
Outdated
} | ||
} | ||
|
||
static bool IsEquivalentContext(StructuralEquivalenceContext &Context, const ContextVector &Contexts1, const ContextVector &Contexts2) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am afraid that we are going to miss too much cases here by implementing this logic all from scratch. A similar logic is already implemented in either NamedDecl::getQualifiedNameAsString or by index::generateUSRForDec. I think we should reuse the USR version ... see my other comment too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The USR seems to be usable but again a string-like (slow?) check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure, I think we should maybe try both solutions.
I like the context vector based solution you have and maybe that is the winner because seems much faster. I am just afraid that we are going to miss something or make errors when implementing that. We can't make big mistakes however when comparing strings.
This PR could possibly supersede #625, right? |
lib/AST/ASTStructuralEquivalence.cpp
Outdated
|
||
using ContextVector = llvm::SmallVector<const DeclContext *, 4>; | ||
|
||
static void GetContexts(ContextVector &Contexts, const NamedDecl *ND) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it handle transparent contexts correctly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How should these be handled? They can be skipped here.
lib/AST/ASTStructuralEquivalence.cpp
Outdated
const DeclContext *DC2 = *DC2I; | ||
++DC2I; | ||
|
||
if (const auto *Spec1 = dyn_cast<ClassTemplateSpecializationDecl>(DC1)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should compare the template args with function template specializations (FunctionDecl with TK_MemberSpecialization TK_FunctionTemplateSpecialization) too.
lib/AST/ASTStructuralEquivalence.cpp
Outdated
OS << *ED; | ||
else | ||
continue;*/ | ||
} else if (const auto *ND1 = dyn_cast<NamedDecl>(DC1)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps we could start with this and then an early return (continue)?
lib/AST/ASTStructuralEquivalence.cpp
Outdated
} | ||
} | ||
|
||
static bool IsEquivalentContext(StructuralEquivalenceContext &Context, const ContextVector &Contexts1, const ContextVector &Contexts2) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd split this function into two:
- comparing all the contexts (basically the for loop)
- comparing two contexts
} | ||
|
||
static bool IsEquivalentContext(StructuralEquivalenceContext &Context, const ContextVector &Contexts1, const ContextVector &Contexts2) { | ||
auto DC2I = Contexts2.begin(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps a check before to see whether the sizes are equal and then an early return if not would make it simpler.
Ctx = Ctx->getParent(); | ||
|
||
// Collect named contexts. | ||
while (Ctx) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems dangerous, what is the parent of a TranslationUnitDecl? Perhaps another guard is needed?
while (Ctx && !Ctx->isTranslationUnit())
Maybe the problems only occur when the scope is explicitly written in source code (like at |
lib/AST/ASTStructuralEquivalence.cpp
Outdated
if (auto *ID = MD->getClassInterface()) | ||
Ctx = ID; | ||
|
||
while (Ctx && Ctx->isFunctionOrMethod()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure that we can skip functions. Think about this case:
namespace A {
void foo() {
struct B { int a; };
}
void bar() {
struct B { int a; };
}
}
Here the first and last B
have a different scope.
Strictly speaking, the equivalency check should handle these cases too, shouldn't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes theoretically it is possible to make such a structural equivalence check. But can this case occur from ASTImporter
? During AST import of bar
it is not possible to find (and compare) the B
in foo
? A DeclContext
outside of foo
should not have references to things inside foo
, or can something "leak" to outside? (A special case can be the "struct definition in function parameter list" case.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, you convinced me, let's skip the functions.
Not necessarily. Consider the below test case, where the same
|
There is another case which we should keep in mind:
Here, even if the using decls have different scopes, the underlying type is the same. So if we just compare the scope of two using decls we might end up having the wrong decision. Probably we have to skip using decls from the scope check. ... And I think this case shows that if we just did a string based (USR or the other) comparison of the params then we would falsely report an inequivalence in this case. ... if we did the scope check with string on the underlying types then it would still work ... As a consequence we can't use the scope check in CheckCommonEquivalence, we have to branch based on the node kind and that means we have to put this to the Update: Just recognized, that the |
I am not sure if this is the best solution but the tests do not fail. The CI tests should be tried. (This is not the final code.)