mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-08-11 02:05:39 +02:00
Bug 534126 - Cache instantiations of alias template instances
This avoid runtime that's exponential in the nesting depth of alias template instances. Change-Id: Ibde6a6d98753df54e8e495a8b4547a90e8313191
This commit is contained in:
parent
45bbb2bb5b
commit
e533381b75
3 changed files with 163 additions and 5 deletions
|
@ -10826,4 +10826,112 @@ public class AST2TemplateTests extends AST2CPPTestBase {
|
||||||
public void testOverloadResolutionWithInitializerList_531322() throws Exception {
|
public void testOverloadResolutionWithInitializerList_531322() throws Exception {
|
||||||
parseAndCheckBindings();
|
parseAndCheckBindings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// using size_t = decltype(sizeof(int));
|
||||||
|
//
|
||||||
|
// template <class Fn, class... Ts>
|
||||||
|
// using MetaApply = typename Fn::template apply<Ts...>;
|
||||||
|
//
|
||||||
|
// template <class... Ts>
|
||||||
|
// struct TypeList {
|
||||||
|
// using type = TypeList;
|
||||||
|
//
|
||||||
|
// template <class Fn>
|
||||||
|
// using apply = MetaApply<Fn, Ts...>;
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// struct Empty {};
|
||||||
|
//
|
||||||
|
// namespace impl {
|
||||||
|
// template <bool B>
|
||||||
|
// struct If_ {
|
||||||
|
// template <class T, class U>
|
||||||
|
// using apply = T;
|
||||||
|
// };
|
||||||
|
// template <>
|
||||||
|
// struct If_<false> {
|
||||||
|
// template <class T, class U>
|
||||||
|
// using apply = U;
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// template <bool If_, class Then, class Else>
|
||||||
|
// using If = MetaApply<impl::If_<If_>, Then, Else>;
|
||||||
|
//
|
||||||
|
// template <template <class...> class C, class... Ts>
|
||||||
|
// class MetaDefer {
|
||||||
|
// template <template <class...> class D = C, class = D<Ts...>>
|
||||||
|
// static char(&try_(int))[1];
|
||||||
|
// static char(&try_(long))[2];
|
||||||
|
// struct Result {
|
||||||
|
// using type = C<Ts...>;
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// public:
|
||||||
|
// template <class... Us>
|
||||||
|
// using apply = typename If<sizeof(try_(0)) - 1 || sizeof...(Us), Empty, Result>::type;
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// struct MetaIdentity {
|
||||||
|
// template <class T>
|
||||||
|
// using apply = T;
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// template <template <class...> class C>
|
||||||
|
// struct MetaQuote {
|
||||||
|
// template <class... Ts>
|
||||||
|
// using apply = MetaApply<MetaDefer<C, Ts...>>;
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// template <>
|
||||||
|
// struct MetaQuote<TypeList> {
|
||||||
|
// template <class... Ts>
|
||||||
|
// using apply = TypeList<Ts...>;
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// template <class Fn>
|
||||||
|
// struct MetaFlip {
|
||||||
|
// template <class A, class B>
|
||||||
|
// using apply = MetaApply<Fn, B, A>;
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// namespace impl {
|
||||||
|
// template <class Fn>
|
||||||
|
// struct FoldL_ {
|
||||||
|
// template <class... Ts>
|
||||||
|
// struct Lambda : MetaIdentity {};
|
||||||
|
// template <class A, class... Ts>
|
||||||
|
// struct Lambda<A, Ts...> {
|
||||||
|
// template <class State>
|
||||||
|
// using apply = MetaApply<Lambda<Ts...>, MetaApply<Fn, State, A>>;
|
||||||
|
// };
|
||||||
|
// template <class... Ts>
|
||||||
|
// using apply = Lambda<Ts...>;
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// template <class List, class State, class Fn>
|
||||||
|
// using TypeReverseFold = MetaApply<MetaApply<List, impl::FoldL_<Fn>>, State>;
|
||||||
|
//
|
||||||
|
// template <class Car, class Cdr = Empty>
|
||||||
|
// struct Cons {};
|
||||||
|
// using Fn = MetaQuote<Cons>;
|
||||||
|
// using T4 = TypeReverseFold<
|
||||||
|
// // Make it long enough to be sure that if the runtime is exponential
|
||||||
|
// // in the length of the list, the test suite times out.
|
||||||
|
// TypeList<int, short, void, float, double, long, char
|
||||||
|
// int*, short*, void*, float*, double*, long*, char*>,
|
||||||
|
// Empty,
|
||||||
|
// MetaFlip<Fn>>;
|
||||||
|
//
|
||||||
|
// template <class T>
|
||||||
|
// struct Dummy {
|
||||||
|
// static const bool value = true;
|
||||||
|
// };
|
||||||
|
// int main() {
|
||||||
|
// static_assert(Dummy<T4>::value, "");
|
||||||
|
// }
|
||||||
|
public void testMetaprogrammingWithAliasTemplates_534126() throws Exception {
|
||||||
|
parseAndCheckBindings();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@ import org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit;
|
||||||
import org.eclipse.cdt.internal.core.dom.parser.IASTAmbiguityParent;
|
import org.eclipse.cdt.internal.core.dom.parser.IASTAmbiguityParent;
|
||||||
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPInheritance.FinalOverriderMap;
|
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPInheritance.FinalOverriderMap;
|
||||||
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
|
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
|
||||||
|
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.TypeInstantiationRequest;
|
||||||
import org.eclipse.cdt.internal.core.index.IIndexScope;
|
import org.eclipse.cdt.internal.core.index.IIndexScope;
|
||||||
import org.eclipse.cdt.internal.core.parser.scanner.InternalFileContent;
|
import org.eclipse.cdt.internal.core.parser.scanner.InternalFileContent;
|
||||||
|
|
||||||
|
@ -55,6 +56,12 @@ public class CPPASTTranslationUnit extends ASTTranslationUnit implements ICPPAST
|
||||||
|
|
||||||
// Caches.
|
// Caches.
|
||||||
private final Map<ICPPClassType, FinalOverriderMap> fFinalOverriderMapCache = new HashMap<>();
|
private final Map<ICPPClassType, FinalOverriderMap> fFinalOverriderMapCache = new HashMap<>();
|
||||||
|
// Cache for type instantiations. This is currently only used for instantiations of
|
||||||
|
// alias template instances, but its use could potentially be expanded to cover other
|
||||||
|
// instantiations. Note that class template instances are already cached by the
|
||||||
|
// template definition, so we wouldn't want to double-cache those. (But we could e.g.
|
||||||
|
// cache instantiations of function types if we found it worthwhile.)
|
||||||
|
private final Map<TypeInstantiationRequest, IType> fInstantiationCache = new HashMap<>();
|
||||||
|
|
||||||
public CPPASTTranslationUnit() {
|
public CPPASTTranslationUnit() {
|
||||||
fScopeMapper= new CPPScopeMapper(this);
|
fScopeMapper= new CPPScopeMapper(this);
|
||||||
|
@ -249,6 +256,10 @@ public class CPPASTTranslationUnit extends ASTTranslationUnit implements ICPPAST
|
||||||
public Map<ICPPClassType, FinalOverriderMap> getFinalOverriderMapCache() {
|
public Map<ICPPClassType, FinalOverriderMap> getFinalOverriderMapCache() {
|
||||||
return fFinalOverriderMapCache;
|
return fFinalOverriderMapCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<TypeInstantiationRequest, IType> getInstantiationCache() {
|
||||||
|
return fInstantiationCache;
|
||||||
|
}
|
||||||
|
|
||||||
public void recordPartialSpecialization(ICPPClassTemplatePartialSpecialization indexSpec,
|
public void recordPartialSpecialization(ICPPClassTemplatePartialSpecialization indexSpec,
|
||||||
ICPPClassTemplatePartialSpecialization astSpec) {
|
ICPPClassTemplatePartialSpecialization astSpec) {
|
||||||
|
|
|
@ -26,6 +26,7 @@ import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUti
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.eclipse.cdt.core.CCorePlugin;
|
import org.eclipse.cdt.core.CCorePlugin;
|
||||||
|
@ -45,6 +46,7 @@ import org.eclipse.cdt.core.dom.ast.IASTName;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTNameOwner;
|
import org.eclipse.cdt.core.dom.ast.IASTNameOwner;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTNode;
|
import org.eclipse.cdt.core.dom.ast.IASTNode;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
|
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
|
||||||
|
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTTypeId;
|
import org.eclipse.cdt.core.dom.ast.IASTTypeId;
|
||||||
import org.eclipse.cdt.core.dom.ast.IArrayType;
|
import org.eclipse.cdt.core.dom.ast.IArrayType;
|
||||||
import org.eclipse.cdt.core.dom.ast.IBinding;
|
import org.eclipse.cdt.core.dom.ast.IBinding;
|
||||||
|
@ -134,6 +136,7 @@ import org.eclipse.cdt.internal.core.dom.parser.ProblemBinding;
|
||||||
import org.eclipse.cdt.internal.core.dom.parser.ProblemFunctionType;
|
import org.eclipse.cdt.internal.core.dom.parser.ProblemFunctionType;
|
||||||
import org.eclipse.cdt.internal.core.dom.parser.ProblemType;
|
import org.eclipse.cdt.internal.core.dom.parser.ProblemType;
|
||||||
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTName;
|
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTName;
|
||||||
|
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTTranslationUnit;
|
||||||
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPAliasTemplateInstance;
|
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPAliasTemplateInstance;
|
||||||
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPAliasTemplateSpecialization;
|
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPAliasTemplateSpecialization;
|
||||||
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPArrayType;
|
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPArrayType;
|
||||||
|
@ -1607,21 +1610,34 @@ public class CPPTemplates {
|
||||||
// to the target type but can SFINAE out during instantiation, so it's not
|
// to the target type but can SFINAE out during instantiation, so it's not
|
||||||
// sufficient to handle it in the ITypeContainer case.
|
// sufficient to handle it in the ITypeContainer case.
|
||||||
if (type instanceof ICPPAliasTemplateInstance) {
|
if (type instanceof ICPPAliasTemplateInstance) {
|
||||||
|
// Cache instantiations of alias templates. This is necessary because below we can
|
||||||
|
// potentially instantiate types that appear in the arguments of an alias template
|
||||||
|
// instance up to three times (once in instantiateArguments(), once in
|
||||||
|
// instantiateArgumentMap(), and if the argument appears in the aliased type, then
|
||||||
|
// a third time in instantiateType()), leading to exponential runtime in cases of
|
||||||
|
// nested alias template instances (which can be common in metaprogramming code
|
||||||
|
// implemented using alias templates).
|
||||||
|
IType result = getCachedInstantiation(instantiationRequest);
|
||||||
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
ICPPAliasTemplateInstance instance = (ICPPAliasTemplateInstance) type;
|
ICPPAliasTemplateInstance instance = (ICPPAliasTemplateInstance) type;
|
||||||
ICPPAliasTemplate template = instance.getTemplateDefinition();
|
ICPPAliasTemplate template = instance.getTemplateDefinition();
|
||||||
ICPPTemplateArgument[] args = instance.getTemplateArguments();
|
ICPPTemplateArgument[] args = instance.getTemplateArguments();
|
||||||
ICPPTemplateArgument[] newArgs = instantiateArguments(args, context, true);
|
ICPPTemplateArgument[] newArgs = instantiateArguments(args, context, true);
|
||||||
if (newArgs == null) {
|
if (newArgs == null) {
|
||||||
return (IType) createProblem(template,
|
result = (IType) createProblem(template,
|
||||||
IProblemBinding.SEMANTIC_INVALID_TEMPLATE_ARGUMENTS);
|
IProblemBinding.SEMANTIC_INVALID_TEMPLATE_ARGUMENTS);
|
||||||
}
|
} else if (args != newArgs) {
|
||||||
if (args != newArgs) {
|
|
||||||
IType target = instantiateType(instance.getType(), context);
|
IType target = instantiateType(instance.getType(), context);
|
||||||
CPPTemplateParameterMap map =
|
CPPTemplateParameterMap map =
|
||||||
instantiateArgumentMap(instance.getTemplateParameterMap(), context);
|
instantiateArgumentMap(instance.getTemplateParameterMap(), context);
|
||||||
return new CPPAliasTemplateInstance(template, target, instance.getOwner(), map, newArgs);
|
result = new CPPAliasTemplateInstance(template, target, instance.getOwner(), map, newArgs);
|
||||||
|
} else {
|
||||||
|
result = type;
|
||||||
}
|
}
|
||||||
return type;
|
putCachedInstantiation(instantiationRequest, result);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type instanceof ITypeContainer) {
|
if (type instanceof ITypeContainer) {
|
||||||
|
@ -3342,4 +3358,27 @@ public class CPPTemplates {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Map<TypeInstantiationRequest, IType> getInstantiationCache() {
|
||||||
|
IASTNode lookupPoint = CPPSemantics.getCurrentLookupPoint();
|
||||||
|
if (lookupPoint != null) {
|
||||||
|
IASTTranslationUnit tu = lookupPoint.getTranslationUnit();
|
||||||
|
if (tu instanceof CPPASTTranslationUnit) {
|
||||||
|
return ((CPPASTTranslationUnit) tu).getInstantiationCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IType getCachedInstantiation(TypeInstantiationRequest instantiationRequest) {
|
||||||
|
Map<TypeInstantiationRequest, IType> cache = getInstantiationCache();
|
||||||
|
return cache != null ? cache.get(instantiationRequest) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void putCachedInstantiation(TypeInstantiationRequest instantiationRequest, IType result) {
|
||||||
|
Map<TypeInstantiationRequest, IType> cache = getInstantiationCache();
|
||||||
|
if (cache != null) {
|
||||||
|
cache.put(instantiationRequest, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue