1
0
Fork 0
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:
Michael Woski 2017-08-27 11:47:36 +02:00
parent eb70c04ceb
commit 4e7354e1b0
5 changed files with 210 additions and 67 deletions

View file

@ -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;

View file

@ -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());

View file

@ -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;

View file

@ -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();

View file

@ -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" });
}
}