diff --git a/Makefile b/Makefile index 91df20a1..9a58602c 100644 --- a/Makefile +++ b/Makefile @@ -136,11 +136,19 @@ qtpl: qtc -dir=./internal -skipLineComments; \ fi -.PHONY: cpp -cpp: build +.PHONY: cpp_build +cpp_build: + g++ -o $(GEN_PATH)/test_cpp $(GEN_PATH)/test_cpp.cpp $(GEN_PATH)/cpp/all.cpp -std=c++17 -O3 -Wno-noexcept-type -g -Wall -Wextra -Werror=return-type -Wno-unused-parameter + +.PHONY: cpp_gen +cpp_gen: build @./target/bin/tlgen -language=cpp -v \ --outdir=./$(GEN_PATH)/cpp \ --generateRPCCode=false \ --basicPkgPath=$(BASIC_TL_PATH) \ ./$(TLS_PATH)/cpp.tl - g++ -o $(GEN_PATH)/test_cpp $(GEN_PATH)/test_cpp.cpp $(GEN_PATH)/cpp/all.cpp -std=c++17 -O3 -Wno-noexcept-type -g -Wall -Wextra -Werror=return-type -Wno-unused-parameter + +.PHONY: cpp +cpp: + $(MAKE) cpp_gen + $(MAKE) cpp_build diff --git a/internal/tlcodegen/test/gen/test_cpp.cpp b/internal/tlcodegen/test/gen/test_cpp.cpp index 855fe481..7e8d9f0c 100644 --- a/internal/tlcodegen/test/gen/test_cpp.cpp +++ b/internal/tlcodegen/test/gen/test_cpp.cpp @@ -1,6 +1,5 @@ #include #include "cpp/a.top2.hpp" -#include "cpp/a.top3.hpp" std::string to_hex(const uint8_t *data, size_t count) { static const char hexdigits[] = "0123456789abcdef"; @@ -16,6 +15,8 @@ std::string to_hex(const uint8_t *data, size_t count) { int main() { basictl::tl_ostream_string str; + basictl::tl_ostream_string str2; + tl2::a::Top2 top2; @@ -23,18 +24,8 @@ int main() { auto & buf = str.get_buffer(); std::cout << top2.tl_name() << ": " << to_hex(reinterpret_cast(buf.data()), buf.size()) << std::endl; - tl2::a::Top3 top3; - top3.n = 2; - // top3.a.a = "Hi!"; - top3.c.b.a.a.push_back(5); - top3.c.b.a.a.push_back(7); - - basictl::tl_ostream_string str2; - - top3.write(str2); - - auto & buf2 = str2.get_buffer(); - std::cout << top3.tl_name() << ": " << to_hex(reinterpret_cast(buf2.data()), buf2.size()) << std::endl; +// top3.write(str2); +// std::cout << top3.tl_name() << ": " << to_hex(reinterpret_cast(buf2.data()), buf2.size()) << std::endl; return 0; } diff --git a/internal/tlcodegen/tlgen.go b/internal/tlcodegen/tlgen.go index 8dd7d633..f1acf975 100644 --- a/internal/tlcodegen/tlgen.go +++ b/internal/tlcodegen/tlgen.go @@ -8,12 +8,14 @@ package tlcodegen import ( "fmt" + "github.com/google/go-cmp/cmp" "io" "log" "os" "path/filepath" "runtime/debug" "sort" + "strconv" "strings" "sync/atomic" @@ -357,9 +359,10 @@ type Gen2 struct { // parsed TL supportedAnnotations map[string]int typeDescriptors map[string][]*tlast.Combinator - singleConstructors map[string]*tlast.Combinator // only types with 1 constructor, no functions - allConstructors map[string]*tlast.Combinator // for printing beautiful errors - allAnnotations []string // position is bit + + singleConstructors map[string]*tlast.Combinator // only types with 1 constructor, no functions + allConstructors map[string]*tlast.Combinator // for printing beautiful errors + allAnnotations []string // position is bit // generation builtinTypes map[string]*TypeRWWrapper @@ -373,6 +376,10 @@ type Gen2 struct { TLO []byte // schema represented in tlo format, described using tls.* combinator Code map[string]string // fileName->Content, split by file names relative to output dir copyrightText string + + // new options + typesInfo *TypesInfo + componentsOrder []int } func (gen *Gen2) InternalPrefix() string { @@ -688,6 +695,9 @@ func (gen *Gen2) WriteToDir(outdir string) error { if string(was) == code { notTouched++ continue + } else { + fmt.Printf("File \"%s\":\n", f) + fmt.Println(cmp.Diff(string(was), code)) } } if filepathName != TlJSONHTML { // not deterministic, do not write marker if json help changed @@ -708,8 +718,11 @@ func (gen *Gen2) WriteToDir(outdir string) error { } } for filepathName := range relativeFiles { - deleted++ f := filepath.Join(outdir, filepathName) + if strings.HasSuffix(f, ".o") { + continue + } + deleted++ if err := os.Remove(f); err != nil { return fmt.Errorf("error deleting previous file %q: %w", f, err) } @@ -1046,6 +1059,9 @@ func GenerateCode(tl tlast.TL, options Gen2Options) (*Gen2, error) { sort.Strings(gen.allAnnotations) } skippedDueToWhitelist := 0 + + gen.typesInfo = processCombinators(gen.allConstructors) + for _, typ := range tl { if GenerateUnusedNatTemplates(typ.Construct.Name.String()) && len(typ.TemplateArguments) == 1 && typ.TemplateArguments[0].IsNat { t := tlast.TypeRef{Type: typ.TypeDecl.Name, PR: typ.TypeDecl.PR} @@ -1129,7 +1145,19 @@ func GenerateCode(tl tlast.TL, options Gen2Options) (*Gen2, error) { fmt.Printf("prevented unwrap of %v\n", v.tlName) } } + + _, order := findAllTypesDependencyComponents(sortedTypes) + gen.componentsOrder = order + // in BeforeCodeGenerationStep we split recursion. Which links will be broken depends on order of nodes visited + for _, v := range sortedTypes { + if len(v.arguments) == 0 { + visitedNodes := make(map[*TypeRWWrapper]int) + currentPath := make([]*TypeRWWrapper, 0) + v.trw.FillRecursiveChildren(visitedNodes, ¤tPath) + } + } + for _, v := range sortedTypes { v.trw.BeforeCodeGenerationStep1() } @@ -1200,3 +1228,522 @@ func GenerateCode(tl tlast.TL, options Gen2Options) (*Gen2, error) { return gen, nil } + +var TypeComparator = func(a, b *TypeRWWrapper) int { + return strings.Compare(a.goGlobalName, b.goGlobalName) +} + +func stabilizeOrder(mp *map[*TypeRWWrapper][]*TypeRWWrapper) (keyOrder []*TypeRWWrapper) { + for k, v := range *mp { + slices.SortFunc(v, TypeComparator) + keyOrder = append(keyOrder, k) + } + slices.SortFunc(keyOrder, TypeComparator) + return +} + +func findAllTypesDependencyComponents(types []*TypeRWWrapper) (map[int]map[int]bool, []int) { + dependencyGraph := make(map[*TypeRWWrapper][]*TypeRWWrapper) + reverseDependencyGraph := make(map[*TypeRWWrapper][]*TypeRWWrapper) + + for _, tpU := range types { + dependencies := tpU.trw.AllTypeDependencies() + for _, tpV := range dependencies { + dependencyGraph[tpU] = append(dependencyGraph[tpU], tpV) + reverseDependencyGraph[tpV] = append(reverseDependencyGraph[tpV], tpU) + } + } + + _ = stabilizeOrder(&dependencyGraph) + _ = stabilizeOrder(&reverseDependencyGraph) + + visitedTypes := make(map[*TypeRWWrapper]bool) + order := make([]*TypeRWWrapper, 0) + for _, tp := range types { + if !visitedTypes[tp] { + findAllTypesDependencyComponentsStep1( + tp, + &visitedTypes, + &dependencyGraph, + &order, + ) + } + } + visitedTypes = make(map[*TypeRWWrapper]bool) + component := 1 + for i := len(order) - 1; i >= 0; i-- { + target := order[i] + if !visitedTypes[target] { + findAllTypesDependencyComponentsStep2( + target, + &visitedTypes, + &reverseDependencyGraph, + component, + ) + component += 1 + } + } + + componentsDeps := make(map[int]map[int]bool) + + for _, tpU := range types { + if _, ok := componentsDeps[tpU.typeComponent]; !ok { + componentsDeps[tpU.typeComponent] = make(map[int]bool) + } + for _, tpV := range tpU.trw.AllTypeDependencies() { + if tpU.typeComponent != tpV.typeComponent { + componentsDeps[tpU.typeComponent][tpV.typeComponent] = true + } + } + } + + componentsOrdered := make([]int, 0) + componentsDepsOrdered := make(map[int][]int) + + for componentId, itsDeps := range componentsDeps { + componentsOrdered = append(componentsOrdered, componentId) + list := make([]int, len(itsDeps)) + for dep := range itsDeps { + list = append(list, dep) + } + sort.Ints(list) + componentsDepsOrdered[componentId] = list + } + + sort.Ints(componentsOrdered) + + compOrder := make([]int, 0) + compVisited := make(map[int]bool) + + for comp := range componentsOrdered { + if !compVisited[comp] { + sortComponents(comp, &compVisited, &componentsDepsOrdered, &compOrder) + } + } + + //slices.Reverse(compOrder) + + return componentsDeps, compOrder +} + +func findAllTypesDependencyComponentsStep1( + tp *TypeRWWrapper, + visited *map[*TypeRWWrapper]bool, + typeDeps *map[*TypeRWWrapper][]*TypeRWWrapper, + order *[]*TypeRWWrapper) { + (*visited)[tp] = true + for _, tpDep := range (*typeDeps)[tp] { + if !(*visited)[tpDep] { + findAllTypesDependencyComponentsStep1(tpDep, visited, typeDeps, order) + } + } + *order = append(*order, tp) +} + +func findAllTypesDependencyComponentsStep2( + tp *TypeRWWrapper, + visited *map[*TypeRWWrapper]bool, + typeDeps *map[*TypeRWWrapper][]*TypeRWWrapper, + component int) { + (*visited)[tp] = true + tp.typeComponent = component + for _, tpDep := range (*typeDeps)[tp] { + if !(*visited)[tpDep] { + findAllTypesDependencyComponentsStep2(tpDep, visited, typeDeps, component) + } + } +} + +func sortComponents(target int, visited *map[int]bool, deps *map[int][]int, order *[]int) { + (*visited)[target] = true + for _, next := range (*deps)[target] { + if !(*visited)[next] { + sortComponents(next, visited, deps, order) + } + } + *order = append(*order, target) +} + +type ConstructorName = tlast.Name +type Constructor struct { + Type *TypeDefinition + Id uint + Name ConstructorName + Fields []tlast.Field +} + +type TypeName = tlast.Name +type TypeDefinition struct { + IsBasic bool + Name TypeName + TypeArguments []tlast.TemplateArgument + Constructors []*Constructor +} + +type EvaluatedTypeVariant = int + +const ( + NumberConstant EvaluatedTypeVariant = 0 + NumberVariable EvaluatedTypeVariant = 1 + TypeConstant EvaluatedTypeVariant = 2 + TypeVariable EvaluatedTypeVariant = 3 +) + +type EvaluatedType struct { + Index EvaluatedTypeVariant + + // union variants + Constant uint32 // 0 + Variable string // 1 + Type *TypeReduction // 2 + TypeVariable string // 3 + + VariableActsAsConstant bool // only if Index == 1 and only for type declarations +} + +type TypeReduction struct { + IsType bool + + Type *TypeDefinition + Constructor *Constructor + + Arguments []EvaluatedType +} + +func (tr *TypeReduction) ReferenceName() (name tlast.Name) { + if tr.IsType { + name = tr.Type.Name + } else { + name = tr.Constructor.Name + } + return +} + +func (tr *TypeReduction) ReferenceType() *TypeDefinition { + if tr.IsType { + return tr.Type + } + return tr.Constructor.Type +} + +func (tr *TypeReduction) String() string { + s := "" + if tr.Type != nil || tr.Constructor != nil { + s += tr.ReferenceName().String() + if len(tr.Arguments) != 0 { + s += "<" + for i, arg := range tr.Arguments { + switch arg.Index { + case 0: + s += "Con" + strconv.FormatInt(int64(arg.Constant), 10) + case 1: + s += "Var" + case 2: + s += arg.Type.String() + case 3: + s += "TypeVar" + } + if len(tr.Arguments) != i+1 { + s += "," + } + } + s += ">" + } + } + return s +} + +func processCombinators(types map[string]*tlast.Combinator) *TypesInfo { + existingTypes := make(map[TypeName]*TypeDefinition) + existingConstructors := make(map[ConstructorName]*Constructor) + + for _, comb := range types { + declaredType := comb.TypeDecl.Name + if comb.Builtin { + declaredType = comb.Construct.Name + } + currentConstructor := comb.Construct.Name + if _, presented := existingTypes[declaredType]; !presented { + existingTypes[declaredType] = &TypeDefinition{ + IsBasic: comb.Builtin, + Name: declaredType, + Constructors: []*Constructor{}, + TypeArguments: comb.TemplateArguments, + } + } + targetType := existingTypes[declaredType] + constructor := Constructor{ + Name: currentConstructor, + Fields: comb.Fields, + Id: uint(len(targetType.Constructors)), + Type: targetType, + } + targetType.Constructors = append(targetType.Constructors, &constructor) + existingConstructors[currentConstructor] = &constructor + } + + typeReductions := make(map[*TypeDefinition]*map[string]*TypeReduction) + + for _, comb := range existingTypes { + if len(comb.TypeArguments) != 0 { + continue + } + reduce( + TypeReduction{ + IsType: true, + Type: existingTypes[comb.Name], + }, + &typeReductions, + &existingTypes, + &existingConstructors, + ) + } + + ti := TypesInfo{Types: existingTypes, Constructors: existingConstructors, TypeReductions: typeReductions} + + //printResults(ti) + + return &ti +} + +func reduce( + targetTypeReduction TypeReduction, + visitedReductions *map[*TypeDefinition]*map[string]*TypeReduction, + types *map[TypeName]*TypeDefinition, + constructors *map[ConstructorName]*Constructor, +) { + for _, arg := range targetTypeReduction.Arguments { + if arg.Index == TypeConstant { + reduce(*arg.Type, visitedReductions, types, constructors) + } + } + + var visitingConstructors []*Constructor + + if targetTypeReduction.IsType { + visitingConstructors = targetTypeReduction.Type.Constructors + } else { + visitingConstructors = append(visitingConstructors, targetTypeReduction.Constructor) + } + + for _, constr := range visitingConstructors { + reduceConstructor( + constr, + targetTypeReduction.Arguments, + visitedReductions, + types, + constructors, + ) + } +} + +func reduceConstructor( + constructor *Constructor, + args []EvaluatedType, + visitedReductions *map[*TypeDefinition]*map[string]*TypeReduction, + types *map[TypeName]*TypeDefinition, + constructors *map[ConstructorName]*Constructor, +) { + if constructor == nil || constructor.Type.IsBasic { + return + } + + trs, ok := (*visitedReductions)[constructor.Type] + if !ok { + tmp := make(map[string]*TypeReduction) + (*visitedReductions)[constructor.Type] = &tmp + trs = &tmp + } + + constructorReduction := TypeReduction{IsType: false, Constructor: constructor, Arguments: args} + + _, ok = (*trs)[constructorReduction.String()] + if !ok { + (*trs)[constructorReduction.String()] = &constructorReduction + } else { + return + } + + defaultFields := calculateDefaultFields(constructor, args) + + for _, field := range constructor.Fields { + fieldType := toTypeReduction(field.FieldType, types, constructors) + if fieldType != nil { + fillTypeReduction(fieldType, args, constructor.Type, &defaultFields) + reduce(*fieldType, visitedReductions, types, constructors) + } + } +} + +func calculateDefaultFields( + constructor *Constructor, + args []EvaluatedType, +) map[string]bool { + defaults := make(map[string]bool) + + for _, field := range constructor.Fields { + if field.Mask != nil { + name := field.Mask.MaskName + bit := field.Mask.BitNumber + if _, ok := defaults[name]; ok { + defaults[field.FieldName] = true + continue + } + if argIndex := findArgByName(name, constructor.Type.TypeArguments); argIndex != -1 { + arg := args[argIndex] + if arg.Index == 0 && (arg.Constant&(1<" + } + return s +} + func (w *TypeRWWrapper) cppTypeStringInNamespace(bytesVersion bool, hppInc *DirectIncludesCPP, halfResolve bool, halfResolved HalfResolvedArgument) (string, string, string) { - hppInc.ns[w.fileName] = struct{}{} + hppInc.ns[w.fileName] = CppIncludeInfo{w.typeComponent} bName := strings.Builder{} // bName.WriteString(w.cppNamespaceQualifier()) bName.WriteString(w.tlName.Name) @@ -545,7 +644,12 @@ outer: type TypeRW interface { // methods below are target language independent markWantsBytesVersion(visitedNodes map[*TypeRWWrapper]bool) - fillRecursiveUnwrap(visitedNodes map[*TypeRWWrapper]bool) + fillRecursiveUnwrap(vistrwitedNodes map[*TypeRWWrapper]bool) + + FillRecursiveChildren(visitedNodes map[*TypeRWWrapper]int, currentPath *[]*TypeRWWrapper) + AllPossibleRecursionProducers() []*TypeRWWrapper + AllTypeDependencies() []*TypeRWWrapper + IsWrappingType() bool BeforeCodeGenerationStep1() // during first phase, some wr.trw are nil due to recursive types. So we delay some BeforeCodeGenerationStep2() // during second phase, union fields recursive bit is set @@ -569,6 +673,7 @@ type TypeRW interface { CPPFillRecursiveChildren(visitedNodes map[*TypeRWWrapper]bool) cppTypeStringInNamespace(bytesVersion bool, hppInc *DirectIncludesCPP) string + cppTypeStringInNamespaceHalfResolved2(bytesVersion bool, typeReduction EvaluatedType) string cppTypeStringInNamespaceHalfResolved(bytesVersion bool, hppInc *DirectIncludesCPP, halfResolved HalfResolvedArgument) string cppDefaultInitializer(halfResolved HalfResolvedArgument, halfResolve bool) string CPPHasBytesVersion() bool diff --git a/internal/tlcodegen/type_rw_bool.go b/internal/tlcodegen/type_rw_bool.go index 7578a531..d112ce4f 100644 --- a/internal/tlcodegen/type_rw_bool.go +++ b/internal/tlcodegen/type_rw_bool.go @@ -42,6 +42,21 @@ func (trw *TypeRWBool) fillRecursiveUnwrap(visitedNodes map[*TypeRWWrapper]bool) func (trw *TypeRWBool) markWantsBytesVersion(visitedNodes map[*TypeRWWrapper]bool) { } +func (trw *TypeRWBool) AllPossibleRecursionProducers() []*TypeRWWrapper { + return nil +} + +func (trw *TypeRWBool) AllTypeDependencies() []*TypeRWWrapper { + return nil +} + +func (trw *TypeRWBool) IsWrappingType() bool { + return true +} + +func (trw *TypeRWBool) FillRecursiveChildren(visitedNodes map[*TypeRWWrapper]int, currentPath *[]*TypeRWWrapper) { +} + func (trw *TypeRWBool) BeforeCodeGenerationStep1() { } diff --git a/internal/tlcodegen/type_rw_bool_cpp.go b/internal/tlcodegen/type_rw_bool_cpp.go index bf75fdb5..e9352cec 100644 --- a/internal/tlcodegen/type_rw_bool_cpp.go +++ b/internal/tlcodegen/type_rw_bool_cpp.go @@ -15,12 +15,16 @@ func (trw *TypeRWBool) CPPFillRecursiveChildren(visitedNodes map[*TypeRWWrapper] } func (trw *TypeRWBool) cppTypeStringInNamespace(bytesVersion bool, hppInc *DirectIncludesCPP) string { - hppInc.ns[trw.wr.fileName] = struct{}{} + hppInc.ns[trw.wr.fileName] = CppIncludeInfo{componentId: trw.wr.typeComponent} return "bool" } func (trw *TypeRWBool) cppTypeStringInNamespaceHalfResolved(bytesVersion bool, hppInc *DirectIncludesCPP, halfResolved HalfResolvedArgument) string { - hppInc.ns[trw.wr.fileName] = struct{}{} + hppInc.ns[trw.wr.fileName] = CppIncludeInfo{componentId: trw.wr.typeComponent} + return "bool" +} + +func (trw *TypeRWBool) cppTypeStringInNamespaceHalfResolved2(bytesVersion bool, typeReduction EvaluatedType) string { return "bool" } @@ -43,7 +47,7 @@ func (trw *TypeRWBool) CPPTypeWritingCode(bytesVersion bool, val string, bare bo func (trw *TypeRWBool) CPPTypeReadingCode(bytesVersion bool, val string, bare bool, natArgs []string, last bool) string { goGlobalName := addBytes(trw.wr.goGlobalName, bytesVersion) - return fmt.Sprintf("\tif (!::%s::%sRead%s(s, %s%s) { return false; }", trw.wr.gen.DetailsCPPNamespace, goGlobalName, addBare(bare), val, joinWithCommas(natArgs)) + return fmt.Sprintf("\tif (!::%s::%sRead%s(s, %s%s)) { return false; }", trw.wr.gen.DetailsCPPNamespace, goGlobalName, addBare(bare), val, joinWithCommas(natArgs)) } func (trw *TypeRWBool) CPPGenerateCode(hpp *strings.Builder, hppInc *DirectIncludesCPP, hppIncFwd *DirectIncludesCPP, hppDet *strings.Builder, hppDetInc *DirectIncludesCPP, cppDet *strings.Builder, cppDetInc *DirectIncludesCPP, bytesVersion bool, forwardDeclaration bool) { diff --git a/internal/tlcodegen/type_rw_maybe.go b/internal/tlcodegen/type_rw_maybe.go index 263b7ed2..6b863daa 100644 --- a/internal/tlcodegen/type_rw_maybe.go +++ b/internal/tlcodegen/type_rw_maybe.go @@ -40,6 +40,24 @@ func (trw *TypeRWMaybe) markWantsBytesVersion(visitedNodes map[*TypeRWWrapper]bo trw.element.t.MarkWantsBytesVersion(visitedNodes) } +func (trw *TypeRWMaybe) AllPossibleRecursionProducers() []*TypeRWWrapper { + return trw.element.t.trw.AllPossibleRecursionProducers() +} + +func (trw *TypeRWMaybe) AllTypeDependencies() []*TypeRWWrapper { + return nil +} + +func (trw *TypeRWMaybe) IsWrappingType() bool { + return true +} + +func (trw *TypeRWMaybe) FillRecursiveChildren(visitedNodes map[*TypeRWWrapper]int, currentPath *[]*TypeRWWrapper) { + visitedNodes[trw.wr] = 1 + trw.element.t.trw.FillRecursiveChildren(visitedNodes, currentPath) + visitedNodes[trw.wr] = 2 +} + func (trw *TypeRWMaybe) BeforeCodeGenerationStep1() { } diff --git a/internal/tlcodegen/type_rw_maybe_cpp.go b/internal/tlcodegen/type_rw_maybe_cpp.go index a2282f91..971960f9 100644 --- a/internal/tlcodegen/type_rw_maybe_cpp.go +++ b/internal/tlcodegen/type_rw_maybe_cpp.go @@ -16,15 +16,19 @@ func (trw *TypeRWMaybe) CPPFillRecursiveChildren(visitedNodes map[*TypeRWWrapper } func (trw *TypeRWMaybe) cppTypeStringInNamespace(bytesVersion bool, hppInc *DirectIncludesCPP) string { - hppInc.ns[trw.wr.fileName] = struct{}{} + hppInc.ns[trw.wr.fileName] = CppIncludeInfo{componentId: trw.wr.typeComponent} return "std::optional<" + trw.element.t.CPPTypeStringInNamespace(bytesVersion, hppInc) + ">" } func (trw *TypeRWMaybe) cppTypeStringInNamespaceHalfResolved(bytesVersion bool, hppInc *DirectIncludesCPP, halfResolved HalfResolvedArgument) string { - hppInc.ns[trw.wr.fileName] = struct{}{} + hppInc.ns[trw.wr.fileName] = CppIncludeInfo{componentId: trw.wr.typeComponent} return "std::optional<" + trw.element.t.CPPTypeStringInNamespaceHalfResolved(bytesVersion, hppInc, halfResolved.Args[0]) + ">" } +func (trw *TypeRWMaybe) cppTypeStringInNamespaceHalfResolved2(bytesVersion bool, typeReduction EvaluatedType) string { + return "std::optional<" + trw.element.t.CPPTypeStringInNamespaceHalfResolved2(bytesVersion, typeReduction.Type.Arguments[0]) + ">" +} + func (trw *TypeRWMaybe) cppDefaultInitializer(halfResolved HalfResolvedArgument, halfResolve bool) string { return "" } @@ -50,6 +54,10 @@ func (trw *TypeRWMaybe) CPPTypeReadingCode(bytesVersion bool, val string, bare b } func (trw *TypeRWMaybe) CPPGenerateCode(hpp *strings.Builder, hppInc *DirectIncludesCPP, hppIncFwd *DirectIncludesCPP, hppDet *strings.Builder, hppDetInc *DirectIncludesCPP, cppDet *strings.Builder, cppDetInc *DirectIncludesCPP, bytesVersion bool, forwardDeclaration bool) { + if forwardDeclaration { + trw.element.t.trw.CPPGenerateCode(hpp, hppInc, hppIncFwd, hppDet, hppDetInc, cppDet, cppDetInc, bytesVersion, true) + return + } goGlobalName := addBytes(trw.wr.goGlobalName, bytesVersion) myFullType := trw.cppTypeStringInNamespace(bytesVersion, hppDetInc) diff --git a/internal/tlcodegen/type_rw_primitive.go b/internal/tlcodegen/type_rw_primitive.go index ce4f50cf..5ce4e0b8 100644 --- a/internal/tlcodegen/type_rw_primitive.go +++ b/internal/tlcodegen/type_rw_primitive.go @@ -58,6 +58,21 @@ func (trw *TypeRWPrimitive) fillRecursiveUnwrap(visitedNodes map[*TypeRWWrapper] func (trw *TypeRWPrimitive) markWantsBytesVersion(visitedNodes map[*TypeRWWrapper]bool) { } +func (trw *TypeRWPrimitive) AllPossibleRecursionProducers() []*TypeRWWrapper { + return nil +} + +func (trw *TypeRWPrimitive) AllTypeDependencies() []*TypeRWWrapper { + return nil +} + +func (trw *TypeRWPrimitive) IsWrappingType() bool { + return true +} + +func (trw *TypeRWPrimitive) FillRecursiveChildren(visitedNodes map[*TypeRWWrapper]int, currentPath *[]*TypeRWWrapper) { +} + func (trw *TypeRWPrimitive) BeforeCodeGenerationStep1() { } diff --git a/internal/tlcodegen/type_rw_primitive_cpp.go b/internal/tlcodegen/type_rw_primitive_cpp.go index bdba8e72..63ee636c 100644 --- a/internal/tlcodegen/type_rw_primitive_cpp.go +++ b/internal/tlcodegen/type_rw_primitive_cpp.go @@ -22,6 +22,10 @@ func (trw *TypeRWPrimitive) cppTypeStringInNamespaceHalfResolved(bytesVersion bo return trw.cppPrimitiveType } +func (trw *TypeRWPrimitive) cppTypeStringInNamespaceHalfResolved2(bytesVersion bool, typeReduction EvaluatedType) string { + return trw.cppPrimitiveType +} + func (trw *TypeRWPrimitive) cppDefaultInitializer(halfResolved HalfResolvedArgument, halfResolve bool) string { return trw.cppDefaultInit } diff --git a/internal/tlcodegen/type_rw_struct.go b/internal/tlcodegen/type_rw_struct.go index cafeb97e..ae9730e2 100644 --- a/internal/tlcodegen/type_rw_struct.go +++ b/internal/tlcodegen/type_rw_struct.go @@ -40,22 +40,22 @@ func (trw *TypeRWStruct) isUnwrapType() bool { if isPrimitive && primitive.tlType == trw.wr.tlName.String() { return true } - brackets, isBuiltinBrackets := trw.Fields[0].t.trw.(*TypeRWBrackets) - if isBuiltinBrackets && (brackets.dictLike || trw.wr.tlName.String() == "vector" || trw.wr.tlName.String() == "tuple") { - return true - } + //brackets, isBuiltinBrackets := trw.Fields[0].t.trw.(*TypeRWBrackets) + //if isBuiltinBrackets && (brackets.dictLike || trw.wr.tlName.String() == "vector" || trw.wr.tlName.String() == "tuple") { + // return true + //} // in combined TL Dictionary is defined via Vector. // dictionaryField {t:Type} key:string value:t = DictionaryField t; // dictionary#1f4c618f {t:Type} %(Vector %(DictionaryField t)) = Dictionary t; // TODO - change combined.tl to use # [] after we fully control generation of C++ & (k)PHP and remove code below - str, isStruct := trw.Fields[0].t.trw.(*TypeRWStruct) - if isStruct && str.wr.tlName.String() == "vector" { - // repeat check above 1 level deeper - brackets, isBuiltinBrackets = str.Fields[0].t.trw.(*TypeRWBrackets) - if isBuiltinBrackets && brackets.dictLike { - return true - } - } + //str, isStruct := trw.Fields[0].t.trw.(*TypeRWStruct) + //if isStruct && str.wr.tlName.String() == "vector" { + // // repeat check above 1 level deeper + // brackets, isBuiltinBrackets := str.Fields[0].t.trw.(*TypeRWBrackets) + // if isBuiltinBrackets && brackets.dictLike { + // return true + // } + //} return false } @@ -80,6 +80,34 @@ func (trw *TypeRWStruct) markHasBytesVersion(visitedNodes map[*TypeRWWrapper]boo return result } +func (trw *TypeRWWrapper) replaceUnwrapHalfResolvedName(topHalfResolved HalfResolvedArgument, name string) string { + if name == "" { + return "" + } + for i, arg := range trw.origTL[0].TemplateArguments { + if arg.FieldName == name { + return topHalfResolved.Args[i].Name + } + } + return "" +} + +// same code as in func (w *TypeRWWrapper) transformNatArgsToChild, replaceUnwrapArgs +func (trw *TypeRWWrapper) replaceUnwrapHalfResolved(topHalfResolved HalfResolvedArgument, halfResolved HalfResolvedArgument) HalfResolvedArgument { + // example + // tuple#9770768a {t:Type} {n:#} [t] = Tuple t n; + // innerMaybe {X:#} a:(Maybe (tuple int X)) = InnerMaybe X; + // when unwrapping we need to change tuple into __tuple + // halfResolved references in field of tuple are to "n", "t" local template args + // we must look up in tuple to replace "n" "t" into "X", "" + var result HalfResolvedArgument + result.Name = trw.replaceUnwrapHalfResolvedName(topHalfResolved, halfResolved.Name) + for _, arg := range halfResolved.Args { + result.Args = append(result.Args, trw.replaceUnwrapHalfResolved(topHalfResolved, arg)) + } + return result +} + func (trw *TypeRWStruct) markWriteHasError(visitedNodes map[*TypeRWWrapper]bool) bool { result := false for _, f := range trw.Fields { @@ -107,12 +135,69 @@ func (trw *TypeRWStruct) markWantsBytesVersion(visitedNodes map[*TypeRWWrapper]b } } -func (trw *TypeRWStruct) BeforeCodeGenerationStep1() { +func (trw *TypeRWStruct) AllPossibleRecursionProducers() []*TypeRWWrapper { + var result []*TypeRWWrapper + for _, typeDep := range trw.wr.arguments { + if typeDep.tip != nil { + result = append(result, typeDep.tip.trw.AllPossibleRecursionProducers()...) + } + } + if !trw.isTypeDef() { + result = append(result, trw.wr) + } + return result +} + +func (trw *TypeRWStruct) AllTypeDependencies() (res []*TypeRWWrapper) { + used := make(map[*TypeRWWrapper]bool) + red := trw.wr.gen.typesInfo.TypeNameToGenericTypeReduction(trw.wr.tlName) + for i, f := range trw.Fields { - visitedNodes := map[*TypeRWWrapper]bool{} - f.t.trw.fillRecursiveChildren(visitedNodes) - trw.Fields[i].recursive = visitedNodes[trw.wr] + fieldRed := trw.wr.gen.typesInfo.FieldTypeReduction(&red, i) + deps := f.t.ActualTypeDependencies(fieldRed) + for _, dep := range deps { + used[dep] = true + } + } + + for tp := range used { + res = append(res, tp) } + return +} + +func (trw *TypeRWStruct) IsWrappingType() bool { + return trw.isUnwrapType() +} + +func (trw *TypeRWStruct) FillRecursiveChildren(visitedNodes map[*TypeRWWrapper]int, currentPath *[]*TypeRWWrapper) { + if visitedNodes[trw.wr] != 0 { + return + } + *currentPath = append(*currentPath, trw.wr) + visitedNodes[trw.wr] = 1 + for i, f := range trw.Fields { + if f.recursive { + continue + } + for _, typeDep := range f.t.trw.AllPossibleRecursionProducers() { + if visitedNodes[typeDep] == 1 { + trw.Fields[i].recursive = true + } else { + typeDep.trw.FillRecursiveChildren(visitedNodes, currentPath) + } + } + } + *currentPath = (*currentPath)[:len(*currentPath)-1] + visitedNodes[trw.wr] = 2 +} + +func (trw *TypeRWStruct) BeforeCodeGenerationStep1() { + //for i, f := range trw.Fields { + // visitedNodes := map[*TypeRWWrapper]bool{} + // f.t.trw.fillRecursiveChildren(visitedNodes) + // trw.Fields[i].recursive = visitedNodes[trw.wr] + //} trw.setNames = make([]string, len(trw.Fields)) trw.clearNames = make([]string, len(trw.Fields)) trw.isSetNames = make([]string, len(trw.Fields)) @@ -372,34 +457,6 @@ outer: return result } -func (trw *TypeRWWrapper) replaceUnwrapHalfResolvedName(topHalfResolved HalfResolvedArgument, name string) string { - if name == "" { - return "" - } - for i, arg := range trw.origTL[0].TemplateArguments { - if arg.FieldName == name { - return topHalfResolved.Args[i].Name - } - } - return "" -} - -// same code as in func (w *TypeRWWrapper) transformNatArgsToChild, replaceUnwrapArgs -func (trw *TypeRWWrapper) replaceUnwrapHalfResolved(topHalfResolved HalfResolvedArgument, halfResolved HalfResolvedArgument) HalfResolvedArgument { - // example - // tuple#9770768a {t:Type} {n:#} [t] = Tuple t n; - // innerMaybe {X:#} a:(Maybe (tuple int X)) = InnerMaybe X; - // when unwrapping we need to change tuple into __tuple - // halfResolved references in field of tuple are to "n", "t" local template args - // we must look up in tuple to replace "n" "t" into "X", "" - var result HalfResolvedArgument - result.Name = trw.replaceUnwrapHalfResolvedName(topHalfResolved, halfResolved.Name) - for _, arg := range halfResolved.Args { - result.Args = append(result.Args, trw.replaceUnwrapHalfResolved(topHalfResolved, arg)) - } - return result -} - func (trw *TypeRWStruct) typeResettingCode(bytesVersion bool, directImports *DirectImports, ins *InternalNamespace, val string, ref bool) string { if trw.isUnwrapType() { return trw.Fields[0].t.TypeResettingCode(bytesVersion, directImports, ins, val, ref) diff --git a/internal/tlcodegen/type_rw_struct_cpp.go b/internal/tlcodegen/type_rw_struct_cpp.go index bcc44408..84cd974e 100644 --- a/internal/tlcodegen/type_rw_struct_cpp.go +++ b/internal/tlcodegen/type_rw_struct_cpp.go @@ -8,6 +8,7 @@ package tlcodegen import ( "fmt" + "golang.org/x/exp/slices" "strings" ) @@ -36,6 +37,14 @@ func (trw *TypeRWStruct) cppTypeStringInNamespaceHalfResolved(bytesVersion bool, return trw.wr.cppNamespaceQualifier() + trw.wr.cppLocalName + args } +func (trw *TypeRWStruct) cppTypeStringInNamespaceHalfResolved2(bytesVersion bool, typeReduction EvaluatedType) string { + if trw.isUnwrapType() { + eval := trw.wr.gen.typesInfo.FieldTypeReduction(typeReduction.Type, 0) + return trw.Fields[0].t.CPPTypeStringInNamespaceHalfResolved2(bytesVersion, eval) + } + return trw.wr.cppNamespaceQualifier() + trw.wr.cppLocalName + trw.wr.cppTypeArguments(bytesVersion, typeReduction.Type) +} + func (trw *TypeRWStruct) cppDefaultInitializer(halfResolved HalfResolvedArgument, halfResolve bool) string { if trw.isUnwrapType() { return trw.Fields[0].t.CPPDefaultInitializer(trw.Fields[0].halfResolved, halfResolve) @@ -80,7 +89,6 @@ func (trw *TypeRWStruct) CPPTypeReadingCode(bytesVersion bool, val string, bare } func (trw *TypeRWStruct) CPPGenerateCode(hpp *strings.Builder, hppInc *DirectIncludesCPP, hppIncFwd *DirectIncludesCPP, hppDet *strings.Builder, hppDetInc *DirectIncludesCPP, cppDet *strings.Builder, cppDetInc *DirectIncludesCPP, bytesVersion bool, forwardDeclaration bool) { - goLocalName := addBytes(trw.wr.goLocalName, bytesVersion) goGlobalName := addBytes(trw.wr.goGlobalName, bytesVersion) //if trw.wr.unionParent != nil && trw.wr.unionParentIsEnum { // return @@ -96,6 +104,17 @@ func (trw *TypeRWStruct) CPPGenerateCode(hpp *strings.Builder, hppInc *DirectInc if trw.wr.tlName.Namespace != "" { typeNamespace = append(typeNamespace, trw.wr.tlName.Namespace) } + + if !forwardDeclaration { + deps := trw.AllTypeDependencies() + slices.SortFunc(deps, TypeComparator) + + for _, typeDep := range deps { + if typeDep.typeComponent == trw.wr.typeComponent { + typeDep.trw.CPPGenerateCode(hpp, nil, nil, nil, hppDetInc, nil, cppDetInc, bytesVersion, true) + } + } + } cppStartNamespace(hpp, typeNamespace) // hpp.WriteString("// " + goLocalName + "\n") - uncommenting will lead to multiple definition error if len(myArgsDecl) != 0 { @@ -106,28 +125,86 @@ func (trw *TypeRWStruct) CPPGenerateCode(hpp *strings.Builder, hppInc *DirectInc cppFinishNamespace(hpp, typeNamespace) return } + + ti := trw.wr.gen.typesInfo + tlName := trw.wr.tlName + + _, isType := ti.Types[tlName] + typeReduction := TypeReduction{IsType: isType} + if isType { + typeReduction.Type = ti.Types[tlName] + } else { + typeReduction.Constructor = ti.Constructors[tlName] + } + for i, arg := range typeReduction.ReferenceType().TypeArguments { + evalArg := EvaluatedType{} + if arg.IsNat { + evalArg.Index = 1 + evalArg.Variable = arg.FieldName + if trw.wr.arguments[i].isArith { + // set true only here + evalArg.VariableActsAsConstant = true + } + } else { + evalArg.Index = 3 + evalArg.TypeVariable = arg.FieldName + } + typeReduction.Arguments = append(typeReduction.Arguments, evalArg) + } + if trw.isTypeDef() { field := trw.Fields[0] - fieldFullType := field.t.CPPTypeStringInNamespaceHalfResolved(bytesVersion, hppInc, field.halfResolved) - hpp.WriteString(fmt.Sprintf("using %s = %s;", trw.wr.cppLocalName, fieldFullType)) + + //if !field.t.origTL[0].Builtin && len(trw.wr.arguments) != 0 { + typeRed := ti.FieldTypeReduction(&typeReduction, 0) + typeDependencies := field.t.ActualTypeDependencies(typeRed) + for _, typeRw := range typeDependencies { + if typeRw.cppLocalName != "" { + hppInc.ns[typeRw.fileName] = CppIncludeInfo{componentId: typeRw.typeComponent} + } + } + hpp.WriteString(fmt.Sprintf("using %s = %s;", trw.wr.cppLocalName, field.t.CPPTypeStringInNamespaceHalfResolved2(bytesVersion, typeRed))) + //} else { + // fieldFullType := field.t.CPPTypeStringInNamespaceHalfResolved(bytesVersion, hppInc, field.halfResolved) + // hpp.WriteString(fmt.Sprintf("using %s = %s;", trw.wr.cppLocalName, fieldFullType)) + //} } else { hpp.WriteString("struct " + trw.wr.cppLocalName + " {\n") - for _, field := range trw.Fields { - fieldFullType := field.t.CPPTypeStringInNamespaceHalfResolved(bytesVersion, hppInc, field.halfResolved) - fieldsMaskComment := "" - if field.fieldMask != nil { - fieldsMaskComment = fmt.Sprintf(" // Conditional: %s.%d", formatNatArgCPP(trw.Fields, *field.fieldMask), field.BitNumber) + for i, field := range trw.Fields { + hppIncByField := DirectIncludesCPP{ns: map[string]CppIncludeInfo{}} + + typeRed := ti.FieldTypeReduction(&typeReduction, i) + for _, typeRw := range trw.Fields[i].t.ActualTypeDependencies(typeRed) { + if typeRw.trw.IsWrappingType() { + continue + } + hppIncByField.ns[typeRw.fileName] = CppIncludeInfo{componentId: typeRw.typeComponent} } + + fieldFullType := field.t.CPPTypeStringInNamespaceHalfResolved2(bytesVersion, typeRed) + fieldsMaskComment := "" + //if field.fieldMask != nil { + // fieldsMaskComment = fmt.Sprintf(" // Conditional: %s.%d", formatNatArgCPP(trw.Fields, *field.fieldMask), field.BitNumber) + //} if field.recursive { + // TODO make better + for includeType, includeInfo := range hppIncByField.ns { + if includeInfo.componentId == trw.wr.typeComponent { + delete(hppIncByField.ns, includeType) + } + } anyRecursive = true // requires destructor in cpp file hpp.WriteString(fmt.Sprintf("\tstd::shared_ptr<%s> %s{};%s\n", fieldFullType, field.cppName, fieldsMaskComment)) } else { hpp.WriteString(fmt.Sprintf("\t%s %s%s;%s\n", fieldFullType, field.cppName, field.t.CPPDefaultInitializer(field.halfResolved, true), fieldsMaskComment)) } + for includeType, includeInfo := range hppIncByField.ns { + hppInc.ns[includeType] = includeInfo + } //hpp.WriteString(fmt.Sprintf("\t// DebugString: %s\n", field.resolvedType.DebugString())) } if anyRecursive { // && len(trw.cppArgs) != 0 - hpp.WriteString(fmt.Sprintf("\n\t~%s() {}\n", goLocalName)) // TODO - move destructor to cpp + hpp.WriteString(fmt.Sprintf("\n\t~%s() {}\n", trw.wr.cppLocalName)) // TODO - move destructor to cpp // cppDet.WriteString(fmt.Sprintf("%s%s::~%s() {}\n", trw.wr.cppNamespaceQualifier, goLocalName, goLocalName)) } if trw.wr.tlTag != 0 { // anonymous square brackets citizens or other exotic type @@ -233,12 +310,12 @@ bool %[1]sWriteBoxed(::basictl::tl_ostream & s, const %[2]s& item%[3]s); cppDet.WriteString(fmt.Sprintf(` bool %[7]s::%[1]sReadBoxed(::basictl::tl_istream & s, %[2]s& item%[3]s) { if (!s.nat_read_exact_tag(0x%08[9]x)) { return false; } - return %[1]sRead(s, item%[8]s); + return %[7]s::%[1]sRead(s, item%[8]s); } bool %[7]s::%[1]sWriteBoxed(::basictl::tl_ostream & s, const %[2]s& item%[3]s) { if (!s.nat_write(0x%08[9]x)) { return false; } - return %[1]sWrite(s, item%[8]s); + return %[7]s::%[1]sWrite(s, item%[8]s); } `, goGlobalName, @@ -251,7 +328,6 @@ bool %[7]s::%[1]sWriteBoxed(::basictl::tl_ostream & s, const %[2]s& item%[3]s) { formatNatArgsCallCPP(trw.wr.NatParams), trw.wr.tlTag, )) - } cppFinishNamespace(hppDet, trw.wr.gen.DetailsCPPNamespaceElements) } diff --git a/internal/tlcodegen/type_rw_tuple.go b/internal/tlcodegen/type_rw_tuple.go index ae0f73e4..ace31aea 100644 --- a/internal/tlcodegen/type_rw_tuple.go +++ b/internal/tlcodegen/type_rw_tuple.go @@ -74,6 +74,30 @@ func isDictionaryElement(wr *TypeRWWrapper) (bool, bool, Field, Field) { return ok, isString, structElement.Fields[0], structElement.Fields[1] } +func (trw *TypeRWBrackets) FillRecursiveChildren(visitedNodes map[*TypeRWWrapper]int, currentPath *[]*TypeRWWrapper) { + for _, typeDep := range trw.AllPossibleRecursionProducers() { + typeDep.trw.FillRecursiveChildren(visitedNodes, currentPath) + } +} + +func (trw *TypeRWBrackets) AllPossibleRecursionProducers() []*TypeRWWrapper { + var result []*TypeRWWrapper + for _, typeDep := range trw.wr.arguments { + if typeDep.tip != nil { + result = append(result, typeDep.tip.trw.AllPossibleRecursionProducers()...) + } + } + return result +} + +func (trw *TypeRWBrackets) AllTypeDependencies() (res []*TypeRWWrapper) { + return nil +} + +func (trw *TypeRWBrackets) IsWrappingType() bool { + return false +} + func (trw *TypeRWBrackets) BeforeCodeGenerationStep1() { if trw.vectorLike { if ok, isString, kf, vf := isDictionaryElement(trw.element.t); ok { diff --git a/internal/tlcodegen/type_rw_tuple_cpp.go b/internal/tlcodegen/type_rw_tuple_cpp.go index ce877df5..e01d5ab4 100644 --- a/internal/tlcodegen/type_rw_tuple_cpp.go +++ b/internal/tlcodegen/type_rw_tuple_cpp.go @@ -19,7 +19,7 @@ func (trw *TypeRWBrackets) CPPFillRecursiveChildren(visitedNodes map[*TypeRWWrap } func (trw *TypeRWBrackets) cppTypeStringInNamespace(bytesVersion bool, hppInc *DirectIncludesCPP) string { - hppInc.ns[trw.wr.fileName] = struct{}{} + hppInc.ns[trw.wr.fileName] = CppIncludeInfo{componentId: trw.wr.typeComponent} //if trw.dictLike && !bytesVersion { // TODO - which arguments must map have is very complicated //return fmt.Sprintf("std::map<%s, %s>", @@ -51,6 +51,19 @@ func (trw *TypeRWBrackets) cppTypeStringInNamespaceHalfResolved(bytesVersion boo return fmt.Sprintf("std::array<%s, %d>", trw.element.t.CPPTypeStringInNamespaceHalfResolved(bytesVersion, hppInc, halfResolved.Args[1]), trw.size) } +func (trw *TypeRWBrackets) cppTypeStringInNamespaceHalfResolved2(bytesVersion bool, typeReduction EvaluatedType) string { + switch len(typeReduction.Type.Arguments) { + case 1: + return fmt.Sprintf("std::vector<%s>", trw.element.t.CPPTypeStringInNamespaceHalfResolved2(bytesVersion, typeReduction.Type.Arguments[0])) + case 2: + if typeReduction.Type.Arguments[0].VariableActsAsConstant { + return fmt.Sprintf("std::array<%s, %s>", trw.element.t.CPPTypeStringInNamespaceHalfResolved2(bytesVersion, typeReduction.Type.Arguments[1]), typeReduction.Type.Arguments[0].Variable) + } + return fmt.Sprintf("std::vector<%s>", trw.element.t.CPPTypeStringInNamespaceHalfResolved2(bytesVersion, typeReduction.Type.Arguments[1])) + } + return "" +} + func (trw *TypeRWBrackets) cppDefaultInitializer(halfResolved HalfResolvedArgument, halfResolve bool) string { if trw.vectorLike || trw.dynamicSize { return "" @@ -84,6 +97,10 @@ func (trw *TypeRWBrackets) CPPTypeReadingCode(bytesVersion bool, val string, bar } func (trw *TypeRWBrackets) CPPGenerateCode(hpp *strings.Builder, hppInc *DirectIncludesCPP, hppIncFwd *DirectIncludesCPP, hppDet *strings.Builder, hppDetInc *DirectIncludesCPP, cppDet *strings.Builder, cppDetInc *DirectIncludesCPP, bytesVersion bool, forwardDeclaration bool) { + if forwardDeclaration { + trw.element.t.trw.CPPGenerateCode(hpp, hppInc, hppIncFwd, hppDet, hppDetInc, cppDet, cppDetInc, bytesVersion, true) + return + } cppStartNamespace(hppDet, trw.wr.gen.DetailsCPPNamespaceElements) hppDetCode := ` diff --git a/internal/tlcodegen/type_rw_union.go b/internal/tlcodegen/type_rw_union.go index ba7dd08b..2dcefc50 100644 --- a/internal/tlcodegen/type_rw_union.go +++ b/internal/tlcodegen/type_rw_union.go @@ -51,15 +51,53 @@ func (trw *TypeRWUnion) markWantsBytesVersion(visitedNodes map[*TypeRWWrapper]bo } } +func (trw *TypeRWUnion) FillRecursiveChildren(visitedNodes map[*TypeRWWrapper]int, currentPath *[]*TypeRWWrapper) { + if visitedNodes[trw.wr] != 0 { + return + } + *currentPath = append(*currentPath, trw.wr) + visitedNodes[trw.wr] = 1 + for _, f := range trw.Fields { + if f.recursive { + continue + } + f.t.trw.FillRecursiveChildren(visitedNodes, currentPath) + } + *currentPath = (*currentPath)[:len(*currentPath)-1] + visitedNodes[trw.wr] = 2 +} + +func (trw *TypeRWUnion) AllPossibleRecursionProducers() []*TypeRWWrapper { + var result []*TypeRWWrapper + for _, typeDep := range trw.wr.arguments { + if typeDep.tip != nil { + result = append(result, typeDep.tip.trw.AllPossibleRecursionProducers()...) + } + } + result = append(result, trw.wr) + return result +} + +func (trw *TypeRWUnion) AllTypeDependencies() (res []*TypeRWWrapper) { + for _, f := range trw.Fields { + res = append(res, f.t) + } + return +} + +func (trw *TypeRWUnion) IsWrappingType() bool { + return false +} + func (trw *TypeRWUnion) BeforeCodeGenerationStep1() { } func (trw *TypeRWUnion) BeforeCodeGenerationStep2() { - for i, f := range trw.Fields { - visitedNodes := map[*TypeRWWrapper]bool{} - f.t.trw.fillRecursiveChildren(visitedNodes) - trw.Fields[i].recursive = visitedNodes[trw.wr] - } + //for i, f := range trw.Fields { + // visitedNodes := map[*TypeRWWrapper]bool{} + // f.t.trw.fillRecursiveChildren(visitedNodes) + // trw.Fields[i].recursive = visitedNodes[trw.wr] + //} //if trw.wr.gen.options.Language == "cpp" { // Temporary solution to benchmark combined tl // var nf []Field // for _, f := range trw.Fields { diff --git a/internal/tlcodegen/type_rw_union_cpp.go b/internal/tlcodegen/type_rw_union_cpp.go index ecedea1a..7aa71def 100644 --- a/internal/tlcodegen/type_rw_union_cpp.go +++ b/internal/tlcodegen/type_rw_union_cpp.go @@ -29,6 +29,10 @@ func (trw *TypeRWUnion) cppTypeStringInNamespaceHalfResolved(bytesVersion bool, return trw.wr.cppNamespaceQualifier() + trw.wr.cppLocalName + args } +func (trw *TypeRWUnion) cppTypeStringInNamespaceHalfResolved2(bytesVersion bool, typeReduction EvaluatedType) string { + return trw.wr.cppNamespaceQualifier() + trw.wr.cppLocalName + trw.wr.cppTypeArguments(bytesVersion, typeReduction.Type) +} + func (trw *TypeRWUnion) cppDefaultInitializer(halfResolved HalfResolvedArgument, halfResolve bool) string { return "" } @@ -49,7 +53,7 @@ func (trw *TypeRWUnion) CPPTypeWritingCode(bytesVersion bool, val string, bare b func (trw *TypeRWUnion) CPPTypeReadingCode(bytesVersion bool, val string, bare bool, natArgs []string, last bool) string { goGlobalName := addBytes(trw.wr.goGlobalName, bytesVersion) - return fmt.Sprintf("\tif (!::%s::%sRead%s(s, %s%s) { return false; }", trw.wr.gen.DetailsCPPNamespace, goGlobalName, addBare(bare), val, joinWithCommas(natArgs)) + return fmt.Sprintf("\tif (!::%s::%sRead%s(s, %s%s)) { return false; }", trw.wr.gen.DetailsCPPNamespace, goGlobalName, addBare(bare), val, joinWithCommas(natArgs)) } func (trw *TypeRWUnion) CPPGenerateCode(hpp *strings.Builder, hppInc *DirectIncludesCPP, hppIncFwd *DirectIncludesCPP, hppDet *strings.Builder, hppDetInc *DirectIncludesCPP, cppDet *strings.Builder, cppDetInc *DirectIncludesCPP, bytesVersion bool, forwardDeclaration bool) { @@ -58,10 +62,25 @@ func (trw *TypeRWUnion) CPPGenerateCode(hpp *strings.Builder, hppInc *DirectIncl _, myArgsDecl := trw.wr.fullyResolvedClassCppNameArgs() myFullType := trw.cppTypeStringInNamespace(bytesVersion, hppDetInc) - fmt.Printf("Ts: %s %s\n", myFullType, strings.Join(myArgsDecl, ", ")) - fmt.Printf(" %s\n", trw.wr.cppLocalName) + //fmt.Printf("Ts: %s %s\n", myFullType, strings.Join(myArgsDecl, ", ")) + //fmt.Printf(" %s\n", trw.wr.cppLocalName) myFullTypeNoPrefix := strings.TrimPrefix(myFullType, "::") // Stupid C++ has sometimes problems with name resolution of definitions + typeNamespace := trw.wr.gen.RootCPPNamespaceElements + if trw.wr.tlName.Namespace != "" { + typeNamespace = append(typeNamespace, trw.wr.tlName.Namespace) + } + + if forwardDeclaration { + cppStartNamespace(hpp, typeNamespace) + if len(myArgsDecl) != 0 { + hpp.WriteString("template<" + strings.Join(myArgsDecl, ", ") + ">\n") + } + hpp.WriteString("struct " + trw.wr.cppLocalName + ";") + cppFinishNamespace(hpp, typeNamespace) + return + } + cppStartNamespace(hppDet, trw.wr.gen.DetailsCPPNamespaceElements) hppDet.WriteString(fmt.Sprintf(` void %[1]sReset(%[2]s& item); @@ -71,15 +90,12 @@ bool %[1]sWriteBoxed(::basictl::tl_ostream & s, const %[2]s& item%[3]s); cppFinishNamespace(hppDet, trw.wr.gen.DetailsCPPNamespaceElements) - for _, field := range trw.Fields { - if field.recursive { - field.t.trw.CPPGenerateCode(hpp, nil, nil, nil, hppDetInc, nil, cppDetInc, bytesVersion, true) + for _, typeDep := range trw.AllTypeDependencies() { + if typeDep.typeComponent == trw.wr.typeComponent { + typeDep.trw.CPPGenerateCode(hpp, nil, nil, nil, hppDetInc, nil, cppDetInc, bytesVersion, true) } } - typeNamespace := trw.wr.gen.RootCPPNamespaceElements - if trw.wr.tlName.Namespace != "" { - typeNamespace = append(typeNamespace, trw.wr.tlName.Namespace) - } + cppStartNamespace(hpp, typeNamespace) if len(myArgsDecl) != 0 { hpp.WriteString("template<" + strings.Join(myArgsDecl, ", ") + ">\n") @@ -125,8 +141,8 @@ static const uint32_t %[1]s_tbl_tl_tag[]{%[3]s}; `, formatNatArgsDeclCPP(trw.wr.NatParams), trw.CPPTypeResettingCode(bytesVersion, "*this"), - trw.CPPTypeReadingCode(bytesVersion, "*this", false, trw.wr.NatParams, true), - trw.CPPTypeWritingCode(bytesVersion, "*this", false, trw.wr.NatParams, true))) + trw.CPPTypeReadingCode(bytesVersion, "*this", false, formatNatArgsAddNat(trw.wr.NatParams), true), + trw.CPPTypeWritingCode(bytesVersion, "*this", false, formatNatArgsAddNat(trw.wr.NatParams), true))) cppDet.WriteString(fmt.Sprintf(` bool %[5]s::read_boxed(::basictl::tl_istream & s%[1]s) { %[3]s @@ -146,8 +162,8 @@ uint32_t %[5]s::tl_tag() const { `, formatNatArgsDeclCPP(trw.wr.NatParams), trw.CPPTypeResettingCode(bytesVersion, "*this"), - trw.CPPTypeReadingCode(bytesVersion, "*this", false, trw.wr.NatParams, true), - trw.CPPTypeWritingCode(bytesVersion, "*this", false, trw.wr.NatParams, true), + trw.CPPTypeReadingCode(bytesVersion, "*this", false, formatNatArgsAddNat(trw.wr.NatParams), true), + trw.CPPTypeWritingCode(bytesVersion, "*this", false, formatNatArgsAddNat(trw.wr.NatParams), true), myFullTypeNoPrefix, goGlobalName)) } @@ -160,7 +176,9 @@ void %[7]s::%[1]sReset(%[2]s& item) { } bool %[7]s::%[1]sReadBoxed(::basictl::tl_istream & s, %[2]s& item%[3]s) { - switch (s.nat_read()) { + uint32_t nat; + s.nat_read(nat); + switch (nat) { %[5]s default: return s.set_error_union_tag(); } @@ -418,9 +436,14 @@ func (trw *TypeRWUnion) CPPWriteFields(bytesVersion bool) string { func (trw *TypeRWUnion) CPPSetters(bytesVersion bool) string { var s strings.Builder + _, myArgsDecl := trw.wr.fullyResolvedClassCppNameArgs() for fieldIndex, field := range trw.Fields { if field.t.IsTrueType() { - s.WriteString(fmt.Sprintf("\tvoid set_%s() { value.emplace<%d>(); }\n", field.cppName, fieldIndex)) + initValue := "" + if len(myArgsDecl) != 0 { + initValue = "{}" + } + s.WriteString(fmt.Sprintf("\tvoid set_%s() { value.emplace<%d>(%s); }\n", field.cppName, fieldIndex, initValue)) } } return s.String()