From db8c7f14d2f74c3db205782acf8222cb7d91a047 Mon Sep 17 00:00:00 2001 From: _ov4 Date: Wed, 21 Oct 2026 06:14:00 +0000 Subject: feat(codegen): instantiate class templates Co-Authored-By: Claude Opus 5.5 --- include/cps/CodeGen.h | 11 ++ include/cps/TypeSystem.h | 2 + lib/CodeGen/CodeGen.cc | 18 +-- lib/CodeGen/Designator.cc | 56 +++++---- lib/CodeGen/TemplateGen.cc | 161 +++++++++++++++++++++++- lib/CodeGen/TypeSystem.cc | 5 +- tests/i/template-classes/case.json | 1 + tests/i/template-classes/expected.txt | 15 +++ tests/i/template-classes/notes.md | 7 ++ tests/i/template-classes/program.cps | 110 ++++++++++++++++ tests/iii/template-class-errors/case.json | 8 ++ tests/iii/template-class-errors/program.cps | 13 ++ tests/iii/template-class-pack-field/case.json | 5 + tests/iii/template-class-pack-field/program.cps | 5 + 14 files changed, 381 insertions(+), 36 deletions(-) create mode 100644 tests/i/template-classes/case.json create mode 100644 tests/i/template-classes/expected.txt create mode 100644 tests/i/template-classes/notes.md create mode 100644 tests/i/template-classes/program.cps create mode 100644 tests/iii/template-class-errors/case.json create mode 100644 tests/iii/template-class-errors/program.cps create mode 100644 tests/iii/template-class-pack-field/case.json create mode 100644 tests/iii/template-class-pack-field/program.cps diff --git a/include/cps/CodeGen.h b/include/cps/CodeGen.h index 23487f6..4730585 100644 --- a/include/cps/CodeGen.h +++ b/include/cps/CodeGen.h @@ -26,6 +26,7 @@ class ExceptionHandler; class CastExprAST; class NewExprAST; class TemplateDeclAST; +class ClassTypeDeclAST; class CodeGen { std::unique_ptr TheContext; @@ -43,6 +44,7 @@ class CodeGen { TemplateDeclAST *Decl; std::string Arguments; std::unique_ptr Function; + std::unique_ptr Class; const FuncSig *Signature; size_t OwnerUnit; size_t RequesterUnit; @@ -52,6 +54,10 @@ class CodeGen { std::map> TemplateSymbols; size_t NextTemplateBody = 0; unsigned TemplateDepth = 0; + bool TypePrePass = false; + bool SpeculativeTemplateResolution = false; + IdentifierSet FailedTemplateTypes; + std::vector DeferredClassPrototypes; bool hasCallable(const std::string &Name) const; bool hasFunctionTemplate(const std::string &Name) const; void validateTemplates(); @@ -59,6 +65,10 @@ class CodeGen { const FuncSig *instantiateFunction(const TemplateDefinition &Definition, const IdentifierMap> &Bindings); void drainTemplateBodies(); + const TypeInfo *instantiateClass(const std::string &Name); + std::optional canonicalTemplateArgument(const std::string &Name); + void emitClassPrototypes(ClassTypeDeclAST *Class); + void emitDeferredClassPrototypes(); std::vector> Exports; bool isVisible(size_t Owner) const; void selectUnit(size_t Unit); @@ -170,6 +180,7 @@ class CodeGen { // TYPE-system registration (Designator.cc) and operators (TypeCheckGen.cc). void runTypePrePass(const std::vector> &Statements, int Phase); + void registerClassMethods(ClassTypeDeclAST *Class); llvm::Value *emitUserKindBinaryOp(int Op, llvm::Value *L, const TypeInfo *LT, llvm::Value *R, const TypeInfo *RT, int Line); diff --git a/include/cps/TypeSystem.h b/include/cps/TypeSystem.h index e4663eb..2d885fa 100644 --- a/include/cps/TypeSystem.h +++ b/include/cps/TypeSystem.h @@ -122,6 +122,7 @@ class TypeSystem { IdentifierMap EnumConstants; std::function Visible; + std::function ResolveMiss; void registerBuiltins(); const TypeInfo *registerType(const std::string &TypeName, llvm::Type *LLVMType, @@ -131,6 +132,7 @@ public: explicit TypeSystem(llvm::LLVMContext &Ctx); void setVisibility(std::function Predicate) { Visible = std::move(Predicate); } + void setResolveMiss(std::function Hook) { ResolveMiss = std::move(Hook); } const TypeInfo *resolve(const std::string &TypeName) const; // Two-pass user-type registration (pass A then pass B, both in source diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc index 9a678ca..fb86120 100644 --- a/lib/CodeGen/CodeGen.cc +++ b/lib/CodeGen/CodeGen.cc @@ -182,9 +182,11 @@ void CodeGen::compile(const std::vector &Sources) { if (Sources.empty()) { reportError("no main program"); return; } Exports.resize(Sources.size()); Types->setVisibility([this](const std::string &Name) { + if (FailedTemplateTypes.count(Name)) return false; auto It = TypeOwners.find(Name); return It == TypeOwners.end() || isVisible(It->second); }); + Types->setResolveMiss([this](const std::string &Name) { return instantiateClass(Name); }); FuncGen->setVisibility([this](size_t Owner) { return isVisible(Owner); }); struct Definition { size_t Unit; bool IsFunction; bool IsOverload; bool IsExtern; bool IsTemplate; int Line; }; @@ -277,6 +279,7 @@ void CodeGen::compile(const std::vector &Sources) { BasicBlock *UnhandledBB = BasicBlock::Create(*TheContext, "unhandled_exception", F); Exceptions->pushHandler(UnhandledBB); + TypePrePass = true; // Reserve names across every unit before resolving any type payload. for (size_t I = 0; I < Sources.size(); ++I) { selectUnit(I); @@ -294,6 +297,8 @@ void CodeGen::compile(const std::vector &Sources) { runTypePrePass(Sources[I].Statements, 2); }; for (size_t I = 0; I < Sources.size(); ++I) DefineTypes(I); + TypePrePass = false; + emitDeferredClassPrototypes(); for (size_t I = 0; I < Sources.size(); ++I) { selectUnit(I); @@ -304,11 +309,7 @@ void CodeGen::compile(const std::vector &Sources) { if (auto *Extern = dynamic_cast(Stmt.get())) FuncGen->emitExternPrototype(Extern->getProto()); if (auto *Class = dynamic_cast(Stmt.get())) { - for (const auto &Method : Class->getMethods()) { - SaveAndRestore MethodLocation(DiagnosticLine, Method->getLine()); - if (isRegisteredMethod(Class->getName(), Method->getProto())) - FuncGen->emitPrototype(Method->getProto()); - } + emitClassPrototypes(Class); } } } @@ -573,14 +574,15 @@ const FuncSig *CodeGen::resolveCall(const std::string &Name, if (HasPackParam ? Actual.size() < FixedCount : Actual.size() != FixedCount) continue; detail::TemplateBindings Bindings; for (const auto &Param : TemplateParams) Bindings.emplace(Param.Name, std::vector{}); + SaveAndRestore Speculation(SpeculativeTemplateResolution, true); bool Match = true; if (HasExplicit) { size_t FixedTypes = TemplateParams.size() - Variadic; if (!Variadic && Explicit->size() > FixedTypes) continue; for (size_t I = 0; I < Explicit->size(); ++I) { - const TypeInfo *Type = resolveType((*Explicit)[I]); - if (!Type || Type->isVoid()) { Match = false; break; } - Bindings.at(TemplateParams[std::min(I, FixedTypes)].Name).push_back(Type->Name); + auto Type = canonicalTemplateArgument((*Explicit)[I]); + if (!Type) { Match = false; break; } + Bindings.at(TemplateParams[std::min(I, FixedTypes)].Name).push_back(*Type); } } if (!Match) continue; diff --git a/lib/CodeGen/Designator.cc b/lib/CodeGen/Designator.cc index 12d3b60..ed43cbe 100644 --- a/lib/CodeGen/Designator.cc +++ b/lib/CodeGen/Designator.cc @@ -42,35 +42,45 @@ void CodeGen::runTypePrePass(const std::vector> &Statem if (!Ok) reportError("%s", Err.c_str()); } - if (Phase == 2) for (const auto &S : Statements) { - auto *Class = dynamic_cast(S.get()); - if (!Class) continue; - auto &Methods = ClassMethods[Class->getName()]; - IdentifierSet Names; - for (const auto &Field : Class->getFields()) { - Names.insert(Field.Name); - if (sameIdentifier(Field.Name, "this")) { - SaveAndRestore Location(DiagnosticLine, Class->getLine()); - reportError("A class field cannot be named this"); - } + if (Phase == 2) for (const auto &S : Statements) + if (auto *Class = dynamic_cast(S.get())) registerClassMethods(Class); +} + +void CodeGen::registerClassMethods(ClassTypeDeclAST *Class) { + auto &Methods = ClassMethods[Class->getName()]; + IdentifierSet Names; + for (const auto &Field : Class->getFields()) { + Names.insert(Field.Name); + if (sameIdentifier(Field.Name, "this")) { + SaveAndRestore Location(DiagnosticLine, Class->getLine()); + reportError("A class field cannot be named this"); } - for (const auto &Method : Class->getMethods()) { - SaveAndRestore Location(DiagnosticLine, Method->getLine()); - const std::string &Name = Method->getProto()->getName(); - if (sameIdentifier(Name, "this")) { - reportError("A method cannot be named this"); - } else if (Names.count(Name) || - (!Methods[Name].empty() && (!Method->getProto()->isOverload() || - !Methods[Name].front()->getProto()->isOverload()))) { - reportError("Duplicate member %s in class %s", Name.c_str(), Class->getName().c_str()); - } else { - Methods[Name].push_back(Method.get()); - } + } + for (const auto &Method : Class->getMethods()) { + SaveAndRestore Location(DiagnosticLine, Method->getLine()); + const std::string &Name = Method->getProto()->getName(); + if (sameIdentifier(Name, "this")) { + reportError("A method cannot be named this"); + } else if (Names.count(Name) || + (!Methods[Name].empty() && (!Method->getProto()->isOverload() || + !Methods[Name].front()->getProto()->isOverload()))) { + reportError("Duplicate member %s in class %s", Name.c_str(), Class->getName().c_str()); + } else { + Methods[Name].push_back(Method.get()); } } } +void CodeGen::emitClassPrototypes(ClassTypeDeclAST *Class) { + for (const auto &Method : Class->getMethods()) { + SaveAndRestore Location(DiagnosticLine, Method->getLine()); + if (isRegisteredMethod(Class->getName(), Method->getProto())) + FuncGen->emitPrototype(Method->getProto()); + } +} + const std::vector *CodeGen::findMethod(const std::string &Class, const std::string &Name) const { + if (Class.find('<') != std::string::npos && !resolveType(Class)) return nullptr; auto C = ClassMethods.find(Class); if (C == ClassMethods.end()) return nullptr; auto M = C->second.find(Name); diff --git a/lib/CodeGen/TemplateGen.cc b/lib/CodeGen/TemplateGen.cc index a3fd4ec..a094c17 100644 --- a/lib/CodeGen/TemplateGen.cc +++ b/lib/CodeGen/TemplateGen.cc @@ -7,6 +7,161 @@ using namespace cps; using namespace cps::detail; using namespace llvm; +namespace { +// Source template lists are nonempty; substitution may produce Empty<>. +bool splitTemplateType(const std::string &Name, std::string &Base, + std::vector &Arguments) { + size_t Open = Name.find('<'); + Base = Name.substr(0, Open); + if (Open == std::string::npos) return true; + if (Name.empty() || Name.back() != '>') return false; + size_t Start = Open + 1; + unsigned Depth = 0; + for (size_t I = Start; I + 1 < Name.size(); ++I) { + if (Name[I] == '<') ++Depth; + if (Name[I] == '>') { + if (!Depth) return false; + --Depth; + } + if (!Depth && Name[I] == ',') { + if (I == Start) return false; + Arguments.push_back(Name.substr(Start, I - Start)); + Start = I + 1; + } + } + if (Depth) return false; + if (Start + 1 < Name.size()) Arguments.push_back(Name.substr(Start, Name.size() - Start - 1)); + else if (Start != Open + 1) return false; + return true; +} +} + +std::optional CodeGen::canonicalTemplateArgument(const std::string &Name) { + SaveAndRestore Speculation(SpeculativeTemplateResolution, true); + if (const TypeInfo *Type = resolveType(Name)) + return Type->isVoid() ? std::nullopt : std::optional(Type->Name); + if (FailedTemplateTypes.count(Name)) return std::nullopt; + std::string Base; + std::vector Arguments; + if (!splitTemplateType(Name, Base, Arguments) || Name.find('<') == std::string::npos) + return std::nullopt; + auto Definitions = Templates.find(Base); + if (Definitions == Templates.end()) return std::nullopt; + for (const auto &Definition : Definitions->second) { + if (!Definition.Decl->getClass() || !isVisible(Definition.OwnerUnit)) continue; + size_t Fixed = Definition.Decl->getParams().size() - Definition.Decl->isVariadic(); + if (Arguments.size() < Fixed || (!Definition.Decl->isVariadic() && Arguments.size() != Fixed)) + return std::nullopt; + std::string Canonical = Definition.Decl->getName() + "<"; + for (size_t I = 0; I < Arguments.size(); ++I) { + auto Argument = canonicalTemplateArgument(Arguments[I]); + if (!Argument) return std::nullopt; + if (I) Canonical += ','; + Canonical += *Argument; + } + return Canonical + ">"; + } + return std::nullopt; +} + +const TypeInfo *CodeGen::instantiateClass(const std::string &Name) { + std::string Base; + std::vector Arguments; + bool Valid = splitTemplateType(Name, Base, Arguments); + auto Definitions = Templates.find(Base); + if (Definitions == Templates.end()) return nullptr; + const TemplateDefinition *Definition = nullptr; + for (const auto &Candidate : Definitions->second) + if (Candidate.Decl->getClass() && isVisible(Candidate.OwnerUnit)) { + Definition = &Candidate; + break; + } + if (!Definition || FailedTemplateTypes.count(Name)) return nullptr; + auto Fail = [&](const std::string &Message) -> const TypeInfo * { + if (!SpeculativeTemplateResolution && FailedTemplateTypes.insert(Name).second) + reportError("%s", Message.c_str()); + return nullptr; + }; + if (Name.find('<') == std::string::npos) + return Fail("Class template " + Base + " requires template arguments"); + if (!Valid) return Fail("Invalid class template type " + Name); + const auto &Params = Definition->Decl->getParams(); + size_t Fixed = Params.size() - Definition->Decl->isVariadic(); + if (Arguments.size() < Fixed || (!Definition->Decl->isVariadic() && Arguments.size() != Fixed)) + return Fail("Wrong number of template arguments for " + Base); + TemplateBindings Bindings; + for (const auto &Param : Params) Bindings.emplace(Param.Name, std::vector{}); + std::string Key; + for (size_t I = 0; I < Arguments.size(); ++I) { + const TypeInfo *Argument = resolveType(Arguments[I]); + if (!Argument || Argument->isVoid()) return Fail("Unknown or invalid template argument " + Arguments[I] + " for " + Base); + if (!Key.empty()) Key += ','; + Key += Argument->Name; + Bindings.at(Params[std::min(I, Fixed)].Name).push_back(Argument->Name); + } + std::string InstanceName = Definition->Decl->getName() + "<" + Key + ">"; + if (FailedTemplateTypes.count(InstanceName)) return nullptr; + for (const auto &Instance : TemplateInstances) + if (Instance.Class && Instance.Decl == Definition->Decl && sameIdentifier(Instance.Arguments, Key)) + return Types->resolve(InstanceName); + // Candidate matching must neither emit diagnostics nor reserve failed types. + // Its actual class type is already available, so an exact match reuses it above. + if (SpeculativeTemplateResolution) return nullptr; + if (TemplateDepth >= 64) return Fail("Template instantiation depth exceeds 64"); + + size_t Requester = VisibilityUnit.value_or(CurrentUnit); + size_t SavedUnit = CurrentUnit; + scope_exit RestoreUnit([&] { selectUnit(SavedUnit); }); + SaveAndRestore> Visibility(VisibilityUnit, Requester); + SaveAndRestore Depth(TemplateDepth, TemplateDepth + 1); + selectUnit(Definition->OwnerUnit); + SaveAndRestore Location(DiagnosticLine, Definition->Decl->getLine()); + TemplateClone Clone(Bindings, [this](int Line, const std::string &Message) { + SaveAndRestore Location(DiagnosticLine, Line ? Line : DiagnosticLine); + reportError("%s", Message.c_str()); + }); + auto Class = Clone.klass(Definition->Decl->getClass(), InstanceName); + if (!Class) { + FailedTemplateTypes.insert(Name); + return nullptr; + } + std::string Error; + if (!Types->declareUserType(InstanceName, TypeKind::Record, Error, true)) return Fail(Error); + TypeOwners[InstanceName] = Definition->OwnerUnit; + // Reserve and cache before fields resolve: recursion observes an opaque record + // and defineRecord diagnoses recursive by-value containment. + auto *ClonedClass = Class.get(); + size_t Index = TemplateInstances.size(); + TemplateInstances.push_back({Definition->Decl, Key, nullptr, std::move(Class), nullptr, + Definition->OwnerUnit, Requester, TemplateDepth}); + if (!Types->defineRecord(InstanceName, ClonedClass->getFields(), Error)) { + Fail(Error); + FailedTemplateTypes.insert(InstanceName); + return nullptr; + } + registerClassMethods(ClonedClass); + if (TypePrePass) DeferredClassPrototypes.push_back(Index); + else emitClassPrototypes(ClonedClass); + return Types->resolve(InstanceName); +} + +void CodeGen::emitDeferredClassPrototypes() { + size_t SavedUnit = CurrentUnit; + scope_exit RestoreUnit([&] { selectUnit(SavedUnit); }); + for (size_t Index : DeferredClassPrototypes) { + const auto &Instance = TemplateInstances[Index]; + auto *Class = Instance.Class.get(); + size_t Requester = Instance.RequesterUnit; + size_t Owner = Instance.OwnerUnit; + unsigned Depth = Instance.Depth; + SaveAndRestore> Visibility(VisibilityUnit, Requester); + SaveAndRestore InstantiationDepth(TemplateDepth, Depth); + selectUnit(Owner); + emitClassPrototypes(Class); + } + DeferredClassPrototypes.clear(); +} + bool CodeGen::hasFunctionTemplate(const std::string &Name) const { auto It = Templates.find(Name); if (It == Templates.end()) return false; @@ -94,7 +249,7 @@ const FuncSig *CodeGen::instantiateFunction(const TemplateDefinition &Definition const FuncSig *Signature = nullptr; for (const auto *Sig : Signatures) if (Sig->Prototype == Function->getProto()) Signature = Sig; - TemplateInstances.push_back({Definition.Decl, Key, std::move(Function), Signature, + TemplateInstances.push_back({Definition.Decl, Key, std::move(Function), nullptr, Signature, Definition.OwnerUnit, Requester, TemplateDepth + 1}); return Signature; } @@ -106,6 +261,7 @@ void CodeGen::drainTemplateBodies() { // Emission may append instances, so keep no vector element references. const auto &Instance = TemplateInstances[NextTemplateBody++]; auto *Function = Instance.Function.get(); + auto *Class = Instance.Class.get(); auto *Declaration = Instance.Decl; size_t Owner = Instance.OwnerUnit; size_t Requester = Instance.RequesterUnit; @@ -114,6 +270,7 @@ void CodeGen::drainTemplateBodies() { SaveAndRestore InstantiationDepth(TemplateDepth, Depth); SaveAndRestore> SymbolContext(Symbols, TemplateSymbols.at(Declaration)); selectUnit(Owner); - emitStmt(Function); + if (Class && !FailedTemplateTypes.count(Class->getName())) emitStmt(Class); + if (Function) emitStmt(Function); } } diff --git a/lib/CodeGen/TypeSystem.cc b/lib/CodeGen/TypeSystem.cc index 6cf0f00..c5db1eb 100644 --- a/lib/CodeGen/TypeSystem.cc +++ b/lib/CodeGen/TypeSystem.cc @@ -111,9 +111,8 @@ bool TypeSystem::sameType(const std::string &Left, const std::string &Right) con const TypeInfo *TypeSystem::resolve(const std::string &TypeName) const { auto It = Types.find(TypeName); - if (It == Types.end() || (Visible && !Visible(TypeName))) { - return nullptr; - } + if (It == Types.end()) return ResolveMiss ? ResolveMiss(TypeName) : nullptr; + if (Visible && !Visible(TypeName)) return nullptr; return &It->second; } diff --git a/tests/i/template-classes/case.json b/tests/i/template-classes/case.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/tests/i/template-classes/case.json @@ -0,0 +1 @@ +{} diff --git a/tests/i/template-classes/expected.txt b/tests/i/template-classes/expected.txt new file mode 100644 index 0000000..9413c45 --- /dev/null +++ b/tests/i/template-classes/expected.txt @@ -0,0 +1,15 @@ +FALSE +0 +7 +3 +2.500000 +30 +20 +10 +5 +9 +alpha +beta +1 +2.500000 +c diff --git a/tests/i/template-classes/notes.md b/tests/i/template-classes/notes.md new file mode 100644 index 0000000..96cfe22 --- /dev/null +++ b/tests/i/template-classes/notes.md @@ -0,0 +1,7 @@ +# Class templates + +Human review: pending. + +TEMPLATE is a compiler extension. Holder forces Box to instantiate during the type pre-pass. Make>() first creates Box through an explicit function argument and returns its default FALSE value. Holder's integer field defaults to zero. Box's explicit constructor then stores 7. Pair preserves its INTEGER and REAL fields, giving 3 and 2.500000. Stack3 pushes 10, 20 and 30 into three fixed scalar slots and pops them in reverse order. + +Keyword NEW constructs a fresh Box containing 5; DELETE releases that heap object. The nested Box copies the inner integer value 9. Box assignment copies its owning STRING field independently: changing Text to beta leaves Copy equal to alpha, and both values remain valid through cleanup. Group expands three method parameters and forwards them to PrintAll, yielding 1, 2.500000 and c. ShowGroup() instantiates an internally empty Group<> and its parameterless Show, producing no output. diff --git a/tests/i/template-classes/program.cps b/tests/i/template-classes/program.cps new file mode 100644 index 0000000..90427d3 --- /dev/null +++ b/tests/i/template-classes/program.cps @@ -0,0 +1,110 @@ +TEMPLATE +CLASS Box + PRIVATE Value : T + PUBLIC OVERLOAD PROCEDURE NEW() + ENDPROCEDURE + PUBLIC OVERLOAD PROCEDURE NEW(V : T) + Value <- V + ENDPROCEDURE + PUBLIC FUNCTION Get() RETURNS T + RETURN Value + ENDFUNCTION + PUBLIC PROCEDURE Set(V : T) + Value <- V + ENDPROCEDURE +ENDCLASS + +TEMPLATE +FUNCTION Make() RETURNS T + DECLARE Value : T + RETURN Value +ENDFUNCTION + +TEMPLATE +CLASS Pair + First : A + Second : B + PROCEDURE NEW(Left : A, Right : B) + First <- Left + Second <- Right + ENDPROCEDURE +ENDCLASS + +TEMPLATE +CLASS Stack3 + PRIVATE First : T + PRIVATE Second : T + PRIVATE Third : T + PRIVATE Count : INTEGER + PROCEDURE Push(Value : T) + Count <- Count + 1 + CASE OF Count + 1 : First <- Value + 2 : Second <- Value + 3 : Third <- Value + ENDCASE + ENDPROCEDURE + FUNCTION Pop() RETURNS T + Count <- Count - 1 + CASE OF Count + 2 : RETURN Third + 1 : RETURN Second + OTHERWISE : RETURN First + ENDCASE + ENDFUNCTION +ENDCLASS + +OVERLOAD PROCEDURE PrintAll() +ENDPROCEDURE +TEMPLATE +OVERLOAD PROCEDURE PrintAll(First : T, Others : Rest...) + OUTPUT First + CALL PrintAll(Others...) +ENDPROCEDURE + +TEMPLATE +CLASS Group + PROCEDURE Show(Values : Types...) + CALL PrintAll(Values...) + ENDPROCEDURE +ENDCLASS +TEMPLATE +PROCEDURE ShowGroup(Values : Types...) + DECLARE Items : Group + CALL Items.Show(Values...) +ENDPROCEDURE + +// Instantiation during the type pre-pass defers the Box method prototypes. +TYPE Holder + DECLARE Item : Box +ENDTYPE +OUTPUT Make>().Get() +DECLARE H : Holder +OUTPUT H.Item.Get() +DECLARE B : Box(7) +OUTPUT B.Get() +DECLARE P : Pair(3, 2.5) +OUTPUT P.First +OUTPUT P.Second +DECLARE S : Stack3 +CALL S.Push(10) +CALL S.Push(20) +CALL S.Push(30) +OUTPUT S.Pop() +OUTPUT S.Pop() +OUTPUT S.Pop() +B <- NEW Box(5) +OUTPUT B.Get() +DELETE B +DECLARE Inner : Box(9) +DECLARE Outer : Box>(Inner) +OUTPUT Outer.Get().Get() +DECLARE Text : Box("alpha") +DECLARE Copy : Box +Copy <- Text +CALL Text.Set("beta") +OUTPUT Copy.Get() +OUTPUT Text.Get() +DECLARE Values : Group +CALL Values.Show(1, 2.5, 'c') +CALL ShowGroup() diff --git a/tests/iii/template-class-errors/case.json b/tests/iii/template-class-errors/case.json new file mode 100644 index 0000000..100438f --- /dev/null +++ b/tests/iii/template-class-errors/case.json @@ -0,0 +1,8 @@ +{ + "errors": [ + {"reason": "No matching overload for Make", "line": 10}, + {"reason": "Wrong number of template arguments for Box", "line": 11}, + {"reason": "Class template Box requires template arguments", "line": 12}, + {"reason": "Unknown or invalid template argument Nope for Box", "line": 13} + ] +} diff --git a/tests/iii/template-class-errors/program.cps b/tests/iii/template-class-errors/program.cps new file mode 100644 index 0000000..67f8db4 --- /dev/null +++ b/tests/iii/template-class-errors/program.cps @@ -0,0 +1,13 @@ +TEMPLATE +CLASS Box + Value : T +ENDCLASS +TEMPLATE +FUNCTION Make() RETURNS T + DECLARE Value : T + RETURN Value +ENDFUNCTION +OUTPUT Make>() +DECLARE TooMany : Box +DECLARE TooFew : Box +DECLARE Unknown : Box diff --git a/tests/iii/template-class-pack-field/case.json b/tests/iii/template-class-pack-field/case.json new file mode 100644 index 0000000..2ad0199 --- /dev/null +++ b/tests/iii/template-class-pack-field/case.json @@ -0,0 +1,5 @@ +{ + "errors": [ + {"reason": "pack.*field|field.*pack", "line": 3} + ] +} diff --git a/tests/iii/template-class-pack-field/program.cps b/tests/iii/template-class-pack-field/program.cps new file mode 100644 index 0000000..83f30bb --- /dev/null +++ b/tests/iii/template-class-pack-field/program.cps @@ -0,0 +1,5 @@ +TEMPLATE +CLASS Invalid + Values : Rest... +ENDCLASS +OUTPUT 0 -- cgit v1.3.1-4-g156e