From 4e7354e1b0826728375f56223cb0c35f03569206 Mon Sep 17 00:00:00 2001 From: Michael Woski Date: Sun, 27 Aug 2017 11:47:36 +0200 Subject: [PATCH] Bug 456224 - CPPDeferredClassInstance not bound to partial template specialization Change-Id: I91482bf3f27becaea796a2c20875f97d92157644 Signed-off-by: Michael Woski --- .../dom/parser/cpp/CPPASTQualifiedName.java | 17 +++ .../parser/cpp/semantics/AccessContext.java | 30 ++-- .../parser/cpp/semantics/CPPTemplates.java | 2 +- .../cpp/semantics/HeuristicResolver.java | 134 +++++++++++------- .../text/contentassist2/CompletionTests.java | 94 +++++++++++- 5 files changed, 210 insertions(+), 67 deletions(-) diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTQualifiedName.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTQualifiedName.java index 8679f34d328..333b01a49de 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTQualifiedName.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTQualifiedName.java @@ -41,9 +41,13 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateId; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDeclaration; import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassScope; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassSpecialization; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassTemplatePartialSpecialization; import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType; import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor; import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPPartialSpecialization; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateDefinition; import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateInstance; import org.eclipse.cdt.core.model.IEnumeration; import org.eclipse.cdt.core.parser.Keywords; @@ -51,6 +55,7 @@ import org.eclipse.cdt.core.parser.util.ArrayUtil; import org.eclipse.cdt.core.parser.util.CharArrayUtils; import org.eclipse.cdt.internal.core.dom.parser.IASTInternalNameOwner; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics; +import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPTemplates; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil; import org.eclipse.cdt.internal.core.parser.util.ContentAssistMatcherFactory; @@ -422,6 +427,18 @@ public class CPPASTQualifiedName extends CPPASTNameBase // This solves bug 456101 (template instance refering to itself). // A:: should not get a binding to its own template definition. continue; + } else if (classType instanceof ICPPDeferredClassInstance + && binding instanceof ICPPClassSpecialization) { + // during heuristic resolution of ICPPDeferredClassInstance's we + // might have found a partial specialization; those have their own template + // definition but share the same primary template as the ICPPDeferredClassInstance + ICPPClassType template = ((ICPPClassSpecialization) binding).getSpecializedBinding(); + if (template instanceof ICPPClassTemplatePartialSpecialization) { + template = (ICPPClassType) ((ICPPClassTemplatePartialSpecialization) template) + .getPrimaryTemplate(); + } + if (template.isSameType((IType) templateDefinition)) + continue; } else if (binding instanceof IType) { if (classType.isSameType((IType) binding)) continue; diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/AccessContext.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/AccessContext.java index 1625e78e331..aca2126bdbc 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/AccessContext.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/AccessContext.java @@ -36,7 +36,7 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPClosureType; import org.eclipse.cdt.internal.core.dom.parser.cpp.ClassTypeHelper; import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPDeferredClassInstance; import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPInternalUnknownScope; -import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownMemberClass; +import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownType; /** * The context that determines access to private and protected class members. @@ -115,14 +115,16 @@ public class AccessContext { while (binding instanceof ICPPSpecialization) { binding = ((ICPPSpecialization) binding).getSpecializedBinding(); } - if (binding instanceof ICPPClassTemplatePartialSpecialization) { - // A class template partial specialization inherits the visibility of its primary - // class template. - binding = ((ICPPClassTemplatePartialSpecialization) binding).getPrimaryClassTemplate(); - } - if (binding instanceof ICPPAliasTemplateInstance) { - binding = ((ICPPAliasTemplateInstance) binding).getTemplateDefinition(); - } + if (binding instanceof ICPPClassTemplatePartialSpecialization) { + // A class template partial specialization requires its primary + // template to be visible + if (!isAccessible( + ((ICPPClassTemplatePartialSpecialization) binding).getPrimaryClassTemplate())) + return false; + } + if (binding instanceof ICPPAliasTemplateInstance) { + binding = ((ICPPAliasTemplateInstance) binding).getTemplateDefinition(); + } IBinding owner = binding.getOwner(); if (owner instanceof CPPClosureType) return true; @@ -300,16 +302,16 @@ public class AccessContext { while (scope != null && !(scope instanceof ICPPClassScope)) { if (scope instanceof ICPPInternalUnknownScope) { IType scopeType = ((ICPPInternalUnknownScope) scope).getScopeType(); - if (scopeType instanceof ICPPDeferredClassInstance) { - return ((ICPPDeferredClassInstance) scopeType).getClassTemplate(); - } - if (scopeType instanceof ICPPUnknownMemberClass && isPrefixLookup) { - scopeType = HeuristicResolver.resolveUnknownType((ICPPUnknownMemberClass) scopeType, + if (scopeType instanceof ICPPUnknownType && isPrefixLookup) { + scopeType = HeuristicResolver.resolveUnknownType((ICPPUnknownType) scopeType, name.getParent()); if (scopeType instanceof ICPPClassType) { return (ICPPClassType) scopeType; } } + if (scopeType instanceof ICPPDeferredClassInstance) { + return ((ICPPDeferredClassInstance) scopeType).getClassTemplate(); + } } scope = CPPSemantics.getParentScope(scope, data.getTranslationUnit()); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPTemplates.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPTemplates.java index 683829e3170..f556d0ed6ef 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPTemplates.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPTemplates.java @@ -2549,7 +2549,7 @@ public class CPPTemplates { return null; } - private static IBinding selectSpecialization(ICPPPartiallySpecializable template, ICPPTemplateArgument[] args, + static IBinding selectSpecialization(ICPPPartiallySpecializable template, ICPPTemplateArgument[] args, boolean isDef, IASTNode point) throws DOMException { if (template == null) { return null; diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/HeuristicResolver.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/HeuristicResolver.java index d01bdf68309..e8d1e76f228 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/HeuristicResolver.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/HeuristicResolver.java @@ -36,6 +36,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPEnumeration; import org.eclipse.cdt.core.dom.ast.cpp.ICPPField; import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod; import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateArgument; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateInstance; import org.eclipse.cdt.core.dom.ast.cpp.ICPPUsingDeclaration; import org.eclipse.cdt.core.parser.util.CharArrayUtils; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPDeferredClassInstance; @@ -143,8 +144,9 @@ public class HeuristicResolver { private static class CPPDependentClassInstance extends CPPDeferredClassInstance implements ICPPClassSpecialization { - public CPPDependentClassInstance(ICPPDeferredClassInstance deferredInstance) { - super(deferredInstance.getClassTemplate(), deferredInstance.getTemplateArguments()); + public CPPDependentClassInstance(ICPPDeferredClassInstance deferredInstance, IASTNode point) { + super(chooseTemplateForDeferredInstance(deferredInstance, point), + deferredInstance.getTemplateArguments()); } @Override @@ -287,7 +289,7 @@ public class HeuristicResolver { break; } else if (lookupType instanceof ICPPDeferredClassInstance) { specializationContext = new CPPDependentClassInstance( - (ICPPDeferredClassInstance) lookupType); + (ICPPDeferredClassInstance) lookupType, point); lookupType = specializationContext.getSpecializedBinding(); break; } @@ -402,62 +404,92 @@ public class HeuristicResolver { } } + /** + * Heuristically choose between the primary template and any partial specializations + * for a deferred template instance. + */ + private static ICPPClassTemplate chooseTemplateForDeferredInstance(ICPPDeferredClassInstance instance, + IASTNode point) { + ICPPClassTemplate template = instance.getClassTemplate(); + if (!instance.isExplicitSpecialization()) { + try { + IBinding partial = CPPTemplates.selectSpecialization(template, + instance.getTemplateArguments(), false, point); + if (partial != null && partial instanceof ICPPTemplateInstance) + return (ICPPClassTemplate) ((ICPPTemplateInstance) partial).getTemplateDefinition(); + } catch (DOMException e) { + } + } + return template; + } + + private static IType resolveEvalType(ICPPEvaluation evaluation, Set lookupSet, + IASTNode point) { + if (evaluation instanceof EvalUnary) { + EvalUnary unary = (EvalUnary) evaluation; + // Handle the common case of a dependent type representing the + // result of dereferencing another dependent type. + if (unary.getOperator() == IASTUnaryExpression.op_star) { + IType argument = unary.getArgument().getType(point); + if (argument instanceof ICPPUnknownType) { + IType resolved = resolveUnknownType((ICPPUnknownType) argument, point); + if (resolved instanceof IPointerType) { + return ((IPointerType) resolved).getType(); + } + } + } + } else if (evaluation instanceof EvalID) { + EvalID id = (EvalID) evaluation; + ICPPEvaluation fieldOwner = id.getFieldOwner(); + if (fieldOwner != null) { + IType fieldOwnerType = fieldOwner.getType(point); + IBinding[] candidates = lookInside(fieldOwnerType, id.isPointerDeref(), id.getName(), + id.getTemplateArgs(), lookupSet, point); + if (candidates.length > 0) { + // If there is more than one candidate, for now just + // choose the first one. A better thing to do would + // be to perform heuristic overload resolution (TODO). + return typeForBinding(candidates[0]); + } + } + } else if (evaluation instanceof EvalFunctionCall) { + EvalFunctionCall evalFunctionCall = (EvalFunctionCall) evaluation; + ICPPEvaluation function = evalFunctionCall.getArguments()[0]; + IType functionType = function.getType(point); + if (functionType instanceof ICPPUnknownType) { + functionType = resolveUnknownType((ICPPUnknownType) functionType, point); + } + return ExpressionTypes.typeFromFunctionCall(functionType); + } else if (evaluation instanceof EvalMemberAccess) { + IBinding member = ((EvalMemberAccess) evaluation).getMember(); + // Presumably the type will be unknown. That's fine, it will be + // resolved during subsequent resolution rounds. + return typeForBinding(member); + } else if (evaluation instanceof EvalTypeId) { + EvalTypeId evalTypeId = (EvalTypeId) evaluation; + IType result = evalTypeId.getInputType(); + if (evalTypeId.representsNewExpression()) { + result = new CPPPointerType(result); + } + return result; + } else if (evaluation instanceof EvalBinding) { + return evaluation.getType(point); + } + // TODO(nathanridge): Handle more cases. + return null; + } + /** * Helper function for {@link #resolveUnknownType} which does one round of resolution. */ private static IType resolveUnknownTypeOnce(ICPPUnknownType type, Set lookupSet, - IASTNode point) { + IASTNode point) { if (type instanceof ICPPDeferredClassInstance) { ICPPDeferredClassInstance deferredInstance = (ICPPDeferredClassInstance) type; - return deferredInstance.getClassTemplate(); + return chooseTemplateForDeferredInstance(deferredInstance, point); } else if (type instanceof TypeOfDependentExpression) { ICPPEvaluation evaluation = ((TypeOfDependentExpression) type).getEvaluation(); - if (evaluation instanceof EvalUnary) { - EvalUnary unary = (EvalUnary) evaluation; - // Handle the common case of a dependent type representing the result of - // dereferencing another dependent type. - if (unary.getOperator() == IASTUnaryExpression.op_star) { - IType argument = unary.getArgument().getType(point); - if (argument instanceof ICPPUnknownType) { - IType resolved = resolveUnknownType((ICPPUnknownType) argument, point); - if (resolved instanceof IPointerType) { - return ((IPointerType) resolved).getType(); - } - } - } - } else if (evaluation instanceof EvalID) { - EvalID id = (EvalID) evaluation; - ICPPEvaluation fieldOwner = id.getFieldOwner(); - if (fieldOwner != null) { - IType fieldOwnerType = fieldOwner.getType(point); - IBinding[] candidates = lookInside(fieldOwnerType, id.isPointerDeref(), id.getName(), - id.getTemplateArgs(), lookupSet, point); - if (candidates.length == 1) { - return typeForBinding(candidates[0]); - } - } - } else if (evaluation instanceof EvalFunctionCall) { - EvalFunctionCall evalFunctionCall = (EvalFunctionCall) evaluation; - ICPPEvaluation function = evalFunctionCall.getArguments()[0]; - IType functionType = function.getType(point); - if (functionType instanceof ICPPUnknownType) { - functionType = resolveUnknownType((ICPPUnknownType) functionType, point); - } - return ExpressionTypes.typeFromFunctionCall(functionType); - } else if (evaluation instanceof EvalMemberAccess) { - IBinding member = ((EvalMemberAccess) evaluation).getMember(); - // Presumably the type will be unknown. That's fine, it will be - // resolved during subsequent resolution rounds. - return typeForBinding(member); - } else if (evaluation instanceof EvalTypeId) { - EvalTypeId evalTypeId = (EvalTypeId) evaluation; - IType result = evalTypeId.getInputType(); - if (evalTypeId.representsNewExpression()) { - result = new CPPPointerType(result); - } - return result; - } - // TODO(nathanridge): Handle more cases. + return resolveEvalType(evaluation, lookupSet, point); } else if (type instanceof ICPPUnknownMemberClass) { ICPPUnknownMemberClass member = (ICPPUnknownMemberClass) type; IType ownerType = member.getOwnerType(); diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/contentassist2/CompletionTests.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/contentassist2/CompletionTests.java index 46147abbacd..ece1fdd3b10 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/contentassist2/CompletionTests.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/contentassist2/CompletionTests.java @@ -1844,6 +1844,56 @@ public class CompletionTests extends CompletionTestBase { assertCompletionResults(fCursorOffset, expected, ID); } + // template + // class allocator { + // public: + // typedef _Tp& reference; + // typedef const _Tp& const_reference; + // + // template + // struct rebind { + // typedef allocator<_Tp1> other; + // }; + // }; + // + // template + // struct __alloc_traits { + // typedef typename _Alloc::reference reference; + // typedef typename _Alloc::const_reference const_reference; + // + // template + // struct rebind { + // typedef typename _Alloc::template rebind<_Tp>::other other; + // }; + // }; + // + // template + // struct _Vector_base { + // typedef typename __alloc_traits<_Alloc>::template + // rebind<_Tp>::other _Tp_alloc_type; + // }; + // + // template > + // class vector { + // typedef _Vector_base<_Tp, _Alloc> _Base; + // typedef typename _Base::_Tp_alloc_type _Tp_alloc_type; + // typedef __alloc_traits <_Tp_alloc_type> _Alloc_traits; + // typedef typename _Alloc_traits::reference reference; + // typedef typename _Alloc_traits::const_reference const_reference; + // public: + // reference front(); + // const_reference front() const; + // + // }; + // + // template + // void foo(vector> v) { + // v.front().f/*cursor*/ + // } + public void testDependentScopes_472818d() throws Exception { + assertCompletionResults(new String[] { "front()" }); + } + // template // struct D { // struct C { @@ -1856,7 +1906,7 @@ public class CompletionTests extends CompletionTestBase { // }; public void testDependentMemberChain_bug478121() throws Exception { setReplaceDotWithArrow(true); - final String[] expected = { "C", "c" }; + final String[] expected = { "c" }; assertCompletionResults(fCursorOffset, expected, ID); assertDotReplacedWithArrow(); } @@ -1931,4 +1981,46 @@ public class CompletionTests extends CompletionTestBase { public void testAliasTemplateTypeSpecifier_521820() throws Exception { assertCompletionResults(new String[] { "Test<>" }); } + + // template + // struct A { + // struct Default { + // }; + // }; + // + // template <> + // template + // struct A<0, J> { + // struct Partial { + // }; + // }; + // + // template + // struct B { + // A<0, I>::/*cursor*/; + // }; + public void testPartialSpecializationWithDeferredClassInstance_456224a() throws Exception { + assertCompletionResults(new String[] { "Partial" }); + } + + // template + // struct A { + // struct Default { + // }; + // }; + // + // template + // struct A<0, J> { + // struct Partial { + // struct Result {}; + // }; + // }; + // + // template + // struct B { + // A<0, I>::Partial::/*cursor*/ + // }; + public void testPartialSpecializationWithDeferredClassInstance_456224b() throws Exception { + assertCompletionResults(new String[] { "Result" }); + } }