mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-06-06 17:26:01 +02:00
Bug 456224 - CPPDeferredClassInstance not bound to partial template
specialization Change-Id: I91482bf3f27becaea796a2c20875f97d92157644 Signed-off-by: Michael Woski <woskimi@yahoo.de>
This commit is contained in:
parent
eb70c04ceb
commit
4e7354e1b0
5 changed files with 210 additions and 67 deletions
|
@ -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<T>:: 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;
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<HeuristicLookup> 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<HeuristicLookup> 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();
|
||||
|
|
|
@ -1844,6 +1844,56 @@ public class CompletionTests extends CompletionTestBase {
|
|||
assertCompletionResults(fCursorOffset, expected, ID);
|
||||
}
|
||||
|
||||
// template<typename _Tp>
|
||||
// class allocator {
|
||||
// public:
|
||||
// typedef _Tp& reference;
|
||||
// typedef const _Tp& const_reference;
|
||||
//
|
||||
// template<typename _Tp1>
|
||||
// struct rebind {
|
||||
// typedef allocator<_Tp1> other;
|
||||
// };
|
||||
// };
|
||||
//
|
||||
// template<typename _Alloc>
|
||||
// struct __alloc_traits {
|
||||
// typedef typename _Alloc::reference reference;
|
||||
// typedef typename _Alloc::const_reference const_reference;
|
||||
//
|
||||
// template<typename _Tp>
|
||||
// struct rebind {
|
||||
// typedef typename _Alloc::template rebind<_Tp>::other other;
|
||||
// };
|
||||
// };
|
||||
//
|
||||
// template<typename _Tp, typename _Alloc>
|
||||
// struct _Vector_base {
|
||||
// typedef typename __alloc_traits<_Alloc>::template
|
||||
// rebind<_Tp>::other _Tp_alloc_type;
|
||||
// };
|
||||
//
|
||||
// template<typename _Tp, typename _Alloc = allocator<_Tp> >
|
||||
// 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<typename T>
|
||||
// void foo(vector<vector<T>> v) {
|
||||
// v.front().f/*cursor*/
|
||||
// }
|
||||
public void testDependentScopes_472818d() throws Exception {
|
||||
assertCompletionResults(new String[] { "front()" });
|
||||
}
|
||||
|
||||
// template <int k>
|
||||
// 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 <int I, int J>
|
||||
// struct A {
|
||||
// struct Default {
|
||||
// };
|
||||
// };
|
||||
//
|
||||
// template <>
|
||||
// template <int J>
|
||||
// struct A<0, J> {
|
||||
// struct Partial {
|
||||
// };
|
||||
// };
|
||||
//
|
||||
// template <int I>
|
||||
// struct B {
|
||||
// A<0, I>::/*cursor*/;
|
||||
// };
|
||||
public void testPartialSpecializationWithDeferredClassInstance_456224a() throws Exception {
|
||||
assertCompletionResults(new String[] { "Partial" });
|
||||
}
|
||||
|
||||
// template <int I, int J>
|
||||
// struct A {
|
||||
// struct Default {
|
||||
// };
|
||||
// };
|
||||
//
|
||||
// template <int J>
|
||||
// struct A<0, J> {
|
||||
// struct Partial {
|
||||
// struct Result {};
|
||||
// };
|
||||
// };
|
||||
//
|
||||
// template <int I>
|
||||
// struct B {
|
||||
// A<0, I>::Partial::/*cursor*/
|
||||
// };
|
||||
public void testPartialSpecializationWithDeferredClassInstance_456224b() throws Exception {
|
||||
assertCompletionResults(new String[] { "Result" });
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue