Skip to content

Commit caacaa8

Browse files
committed
Worked around duplication of types nested in templates and forwarded.
Since all template specialisations are incomplete by default, so are classes nested in them. When such classes are also forwarded, there are two incomplete declarations with the same name and in the same scope. Our parser searches by name and completion and it can therefore not make a difference between the two. Consequently, it always returns the first type it finds even if it isn't the right one. When clang::Sema later completes the specialisations, it completes the nested types too which leads to two identical complete types in the same scope. Signed-off-by: Dimitar Dobrev <[email protected]>
1 parent ed0c21a commit caacaa8

File tree

7 files changed

+60
-9
lines changed

7 files changed

+60
-9
lines changed

src/AST/Class.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,11 @@ public List<ClassTemplateSpecialization> Specializations
270270
}
271271
}
272272

273+
public bool IsTemplate
274+
{
275+
get { return IsDependent && TemplateParameters.Count > 0; }
276+
}
277+
273278
public override IEnumerable<Function> FindOperator(CXXOperatorKind kind)
274279
{
275280
return Methods.Where(m => m.OperatorKind == kind);

src/Generator/Generators/CSharp/CSharpSources.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,16 @@ classTemplate.OriginalNamespace is Class ?
271271
GenerateClassInternals(specialization);
272272

273273
foreach (var group in generated.SelectMany(s => s.Classes).Where(
274-
c => !c.IsIncomplete).GroupBy(c => c.Name))
274+
// HACK: the distinction is needed to eliminate duplicate nested types
275+
// Since all template specialisations are incomplete by default,
276+
// so are classes nested in them.
277+
// When such classes are also forwarded, there are two incomplete declarations
278+
// with the same name and in the same scope. Our parser searches by name and
279+
// completion and it can therefore not make a difference between the two.
280+
// Consequently, it always returns the first type it finds even if it isn't the right one.
281+
// When clang::Sema later completes the specialisations, it completes the nested
282+
// types too which leads to two identical complete types in the same scope.
283+
c => !c.IsIncomplete).Distinct().GroupBy(c => c.Name))
275284
GenerateNestedInternals(group.Key, group);
276285

277286
WriteCloseBraceIndent();
@@ -329,7 +338,7 @@ public override bool VisitClassDecl(Class @class)
329338
foreach (var nestedTemplate in @class.Classes.Where(c => !c.IsIncomplete && c.IsDependent))
330339
GenerateClassTemplateSpecializationInternal(nestedTemplate);
331340

332-
if (@class.IsDependent)
341+
if (@class.IsTemplate)
333342
{
334343
if (!(@class.Namespace is Class))
335344
GenerateClassTemplateSpecializationInternal(@class);

src/Generator/Generators/CSharp/CSharpTypePrinter.cs

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,7 @@ public override TypePrinterResult VisitClassDecl(Class @class)
570570
Helpers.InternalStruct}{Helpers.GetSuffixForInternal(@class)}";
571571

572572
var printed = VisitDeclaration(@class).Type;
573-
if (!@class.IsDependent)
573+
if (!@class.IsTemplate)
574574
return printed;
575575
return $@"{printed}<{string.Join(", ",
576576
@class.TemplateParameters.Select(p => p.Name))}>";
@@ -638,9 +638,7 @@ string GetName(Declaration decl)
638638

639639
while (!(ctx is TranslationUnit))
640640
{
641-
var isInlineNamespace = ctx is Namespace && ((Namespace)ctx).IsInline;
642-
if (!string.IsNullOrWhiteSpace(ctx.Name) && !isInlineNamespace)
643-
names.Push(ctx.Name);
641+
AddContextName(names, ctx);
644642

645643
ctx = ctx.Namespace;
646644
}
@@ -802,6 +800,38 @@ private static bool IsValid(TemplateArgument a)
802800
return templateParam == null || templateParam.Parameter != null;
803801
}
804802

803+
private void AddContextName(Stack<string> names, Declaration ctx)
804+
{
805+
var isInlineNamespace = ctx is Namespace && ((Namespace) ctx).IsInline;
806+
if (string.IsNullOrWhiteSpace(ctx.Name) || isInlineNamespace)
807+
return;
808+
809+
if (ContextKind != TypePrinterContextKind.Managed)
810+
{
811+
names.Push(ctx.Name);
812+
return;
813+
}
814+
815+
string name = null;
816+
var @class = ctx as Class;
817+
if (@class != null)
818+
{
819+
if (@class.IsTemplate)
820+
name = $@"{ctx.Name}<{string.Join(", ",
821+
@class.TemplateParameters.Select(p => p.Name))}>";
822+
else
823+
{
824+
var specialization = @class as ClassTemplateSpecialization;
825+
if (specialization != null)
826+
{
827+
name = $@"{ctx.Name}<{string.Join(", ",
828+
specialization.Arguments.Select(VisitTemplateArgument))}>";
829+
}
830+
}
831+
}
832+
names.Push(name ?? ctx.Name);
833+
}
834+
805835
private CSharpExpressionPrinter expressionPrinter => new CSharpExpressionPrinter(this);
806836
}
807837
}

src/Generator/Passes/CheckIgnoredDecls.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public override bool VisitClassDecl(Class @class)
3636
if (@class.IsInjected)
3737
injectedClasses.Add(@class);
3838

39-
if (!@class.IsDependent)
39+
if (!@class.IsTemplate)
4040
return true;
4141

4242
if (Options.GenerateClassTemplates)

src/Generator/Passes/TrimSpecializationsPass.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public override bool VisitClassDecl(Class @class)
3737
if (!base.VisitClassDecl(@class))
3838
return false;
3939

40-
if (@class.IsDependent)
40+
if (@class.IsTemplate)
4141
{
4242
templates.Add(@class);
4343
foreach (var specialization in @class.Specializations.Where(

tests/CSharp/CSharp.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System;
2-
using System.Collections.Generic;
32
using System.Linq;
43
using System.Text.RegularExpressions;
54
using CppSharp.AST;

tests/CSharp/CSharpTemplates.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ class DLL_API DependentValueFields : public Base<T>
136136
DependentValueFields operator+(const DependentValueFields& other);
137137
T getDependentValue();
138138
void setDependentValue(const T& value);
139+
IndependentFields<Nested> returnNestedInTemplate();
139140
private:
140141
T field{};
141142
union {
@@ -165,6 +166,12 @@ void DependentValueFields<T>::setDependentValue(const T& value)
165166
field = value;
166167
}
167168

169+
template <typename T>
170+
IndependentFields<typename DependentValueFields<T>::Nested> DependentValueFields<T>::returnNestedInTemplate()
171+
{
172+
return DependentValueFields<T>::Nested();
173+
}
174+
168175
template <typename T>
169176
DependentValueFields<T>& DependentValueFields<T>::returnInjectedClass()
170177
{
@@ -532,6 +539,7 @@ template class DLL_API IndependentFields<T1>;
532539
template class DLL_API IndependentFields<std::string>;
533540
template class DLL_API Base<int>;
534541
template class DLL_API DependentValueFields<int>;
542+
template DLL_API IndependentFields<DependentValueFields<int>::Nested> DependentValueFields<int>::returnNestedInTemplate();
535543
template class DLL_API VirtualTemplate<int>;
536544
template class DLL_API VirtualTemplate<bool>;
537545
template class DLL_API HasDefaultTemplateArgument<int, int>;

0 commit comments

Comments
 (0)