diff --git a/frontend/include/chpl/resolution/resolution-queries.h b/frontend/include/chpl/resolution/resolution-queries.h index 116e0e8af911..15fe86733f26 100644 --- a/frontend/include/chpl/resolution/resolution-queries.h +++ b/frontend/include/chpl/resolution/resolution-queries.h @@ -487,6 +487,21 @@ const TypedFnSignature* tryResolveInitEq(Context* context, const types::Type* rhsType, const PoiScope* poiScope = nullptr); +// helper for tryResolveZeroArgInit: add substitutions from a type to a list +// of actuals. In practice, "zero-arg" init calls are really init calls where +// arguments are given by the field substitutions etc. +bool addExistingSubstitutionsAsActuals(Context* context, + const types::Type* type, + std::vector& outActuals, + std::vector& outActualAsts); + + +// tries to resolve an (unambiguous) init() +const TypedFnSignature* tryResolveZeroArgInit(Context* context, + const uast::AstNode* astForScopeOrErr, + const types::Type* toInit, + const PoiScope* poiScope = nullptr); + // tries to resolve an (unambiguous) assign const TypedFnSignature* tryResolveAssign(Context* context, const uast::AstNode* astForScopeOrErr, diff --git a/frontend/include/chpl/types/Type.h b/frontend/include/chpl/types/Type.h index 3b929b49a08c..242a95fc9b82 100644 --- a/frontend/include/chpl/types/Type.h +++ b/frontend/include/chpl/types/Type.h @@ -347,6 +347,8 @@ class Type { */ static bool isPod(Context* context, const Type* t); + static bool isDefaultInitializable(Context* context, const Type* t); + static bool needsInitDeinitCall(const Type* t); /// \cond DO_NOT_DOCUMENT diff --git a/frontend/lib/resolution/Resolver.cpp b/frontend/lib/resolution/Resolver.cpp index abd856a02108..d4a5d1cfc238 100644 --- a/frontend/lib/resolution/Resolver.cpp +++ b/frontend/lib/resolution/Resolver.cpp @@ -2490,31 +2490,6 @@ void Resolver::resolveTupleDecl(const TupleDecl* td, resolveTupleUnpackDecl(td, useT); } -static bool addExistingSubstitutionsAsActuals(Context* context, - const Type* type, - std::vector& outActuals, - std::vector& outActualAsts) { - bool addedSubs = false; - while (auto ct = type->getCompositeType()) { - if (!ct->instantiatedFromCompositeType()) break; - - for (auto& [id, qt] : ct->substitutions()) { - auto fieldName = parsing::fieldIdToName(context, id); - addedSubs = true; - outActuals.emplace_back(qt, fieldName); - outActualAsts.push_back(nullptr); - } - - if (auto clt = ct->toBasicClassType()) { - type = clt->parentClassType(); - } else { - break; - } - } - - return addedSubs; -} - static void findMismatchedInstantiations(Context* context, const CompositeType* originalCT, const CompositeType* finalCT, diff --git a/frontend/lib/resolution/call-init-deinit.cpp b/frontend/lib/resolution/call-init-deinit.cpp index 35242f02f8c8..ef0910694187 100644 --- a/frontend/lib/resolution/call-init-deinit.cpp +++ b/frontend/lib/resolution/call-init-deinit.cpp @@ -22,6 +22,7 @@ #include "chpl/parsing/parsing-queries.h" #include "chpl/resolution/can-pass.h" #include "chpl/resolution/ResolvedVisitor.h" +#include "chpl/resolution/resolution-queries.h" #include "chpl/resolution/resolution-types.h" #include "chpl/resolution/scope-queries.h" #include "chpl/resolution/copy-elision.h" @@ -427,6 +428,7 @@ void CallInitDeinit::resolveDefaultInit(const VarLikeDecl* ast, RV& rv) { // try to resolve 'init' // TODO: handle instantiations passing field types std::vector actuals; + std::vector ignoredActualAsts; actuals.push_back(CallInfoActual(varType, USTR("this"))); if (classType != nullptr && classType->manager() != nullptr) { // when default-initializing a shared C? or owned C?, @@ -444,17 +446,8 @@ void CallInitDeinit::resolveDefaultInit(const VarLikeDecl* ast, RV& rv) { CallInfoActual(QualifiedType(QualifiedType::TYPE, t), chpl_t)); } else if (compositeType != nullptr && compositeType->instantiatedFromCompositeType() != nullptr) { - // pass generic type and param fields by the name - auto subs = compositeType->sortedSubstitutions(); - for (const auto& pair : subs) { - const ID& id = pair.first; - const QualifiedType& qt = pair.second; - auto fieldAst = parsing::idToAst(context, id)->toVarLikeDecl(); - if (fieldAst->storageKind() == QualifiedType::TYPE || - fieldAst->storageKind() == QualifiedType::PARAM) { - actuals.push_back(CallInfoActual(qt, fieldAst->name())); - } - } + addExistingSubstitutionsAsActuals(context, compositeType, actuals, + ignoredActualAsts); } // Get the 'root' instantiation diff --git a/frontend/lib/resolution/prims.cpp b/frontend/lib/resolution/prims.cpp index 0490d77a2535..94534b8f057d 100644 --- a/frontend/lib/resolution/prims.cpp +++ b/frontend/lib/resolution/prims.cpp @@ -1059,6 +1059,12 @@ static QualifiedType primIsPod(Context* context, const CallInfo& ci) { }); } +static QualifiedType primIsDefaultInitializable(Context* context, const CallInfo& ci) { + return actualTypeHasProperty(context, ci, [=](auto t) { + return Type::isDefaultInitializable(context, t); + }); +} + static QualifiedType primNeedsAutoDestroy(Context* context, const CallInfo& ci) { return actualTypeHasProperty(context, ci, [=](auto t) { return Type::needsInitDeinitCall(t) && !Type::isPod(context, t); @@ -1323,7 +1329,7 @@ CallResolutionResult resolvePrimCall(ResolutionContext* rc, break; case PRIM_HAS_DEFAULT_VALUE: - CHPL_UNIMPL("various primitives"); + type = primIsDefaultInitializable(context, ci); break; case PRIM_NEEDS_AUTO_DESTROY: diff --git a/frontend/lib/resolution/resolution-queries.cpp b/frontend/lib/resolution/resolution-queries.cpp index 4e1d5fa74a4e..23caed4b4613 100644 --- a/frontend/lib/resolution/resolution-queries.cpp +++ b/frontend/lib/resolution/resolution-queries.cpp @@ -5498,6 +5498,60 @@ const TypedFnSignature* tryResolveInitEq(Context* context, return c.mostSpecific().only().fn(); } +bool addExistingSubstitutionsAsActuals(Context* context, + const types::Type* type, + std::vector& outActuals, + std::vector& outActualAsts) { + bool addedSubs = false; + while (auto ct = type->getCompositeType()) { + if (!ct->instantiatedFromCompositeType()) break; + + for (auto& [id, qt] : ct->substitutions()) { + auto fieldAst = parsing::idToAst(context, id)->toVarLikeDecl(); + if (fieldAst->storageKind() == QualifiedType::TYPE || + fieldAst->storageKind() == QualifiedType::PARAM) { + addedSubs = true; + outActuals.emplace_back(qt, fieldAst->name()); + outActualAsts.push_back(nullptr); + } + } + + if (auto clt = ct->toBasicClassType()) { + type = clt->parentClassType(); + } else { + break; + } + } + + return addedSubs; +} + +const TypedFnSignature* tryResolveZeroArgInit(Context* context, + const AstNode* astForScopeOrErr, + const types::Type* toInit, + const PoiScope* poiScope) { + if (!toInit->getCompositeType()) return nullptr; + + QualifiedType toInitQt(QualifiedType::INIT_RECEIVER, toInit); + + std::vector actuals; + std::vector ignoredActualAsts; + actuals.push_back(CallInfoActual(toInitQt, USTR("this"))); + addExistingSubstitutionsAsActuals(context, toInit, actuals, ignoredActualAsts); + auto ci = CallInfo(/* name */ USTR("init"), + /* calledType */ toInitQt, + /* isMethodCall */ true, + /* hasQuestionArg */ false, + /* isParenless */ false, actuals); + + const Scope* scope = nullptr; + if (astForScopeOrErr) scope = scopeForId(context, astForScopeOrErr->id()); + + auto c = resolveGeneratedCall(context, astForScopeOrErr, ci, + CallScopeInfo::forNormalCall(scope, poiScope)); + return c.mostSpecific().only().fn(); +} + const TypedFnSignature* tryResolveDeinit(Context* context, const AstNode* astForScopeOrErr, const types::Type* t, diff --git a/frontend/lib/types/Type.cpp b/frontend/lib/types/Type.cpp index 441b08368ab0..c3a5b61110eb 100644 --- a/frontend/lib/types/Type.cpp +++ b/frontend/lib/types/Type.cpp @@ -22,6 +22,7 @@ #include "chpl/framework/query-impl.h" #include "chpl/types/AnyClassType.h" #include "chpl/types/AnyType.h" +#include "chpl/types/ArrayType.h" #include "chpl/types/BoolType.h" #include "chpl/types/BuiltinType.h" #include "chpl/types/CStringType.h" @@ -44,6 +45,7 @@ #include "chpl/types/VoidType.h" #include "chpl/parsing/parsing-queries.h" #include "chpl/resolution/resolution-queries.h" +#include "../resolution/default-functions.h" namespace chpl { namespace types { @@ -258,43 +260,59 @@ const CompositeType* Type::getCompositeType() const { return nullptr; } -static bool -compositeTypeIsPod(Context* context, const Type* t) { +template +static bool checkFieldsWithPredicate(Context* context, const Type* t, F&& pred) { using namespace resolution; - if (auto cls = t->toClassType()) { - return !cls->decorator().isManaged(); - } - auto ct = t->getCompositeType(); - if (!ct) return false; + CHPL_ASSERT(ct); if (auto tt = t->toTupleType()) { - // A tuple is plain old data if all of its components are plain old data. for (int i = 0; i < tt->numElements(); i++) { auto& eltType = tt->elementType(i); if (!eltType.type()) return false; - if (!Type::isPod(context, eltType.type())) return false; + if (!pred(context, eltType.type())) return false; } return true; } - const uast::AstNode* ast = nullptr; - if (auto id = ct->id()) ast = parsing::idToAst(context, std::move(id)); - auto& rf = fieldsForTypeDecl(context, ct, DefaultsPolicy::USE_DEFAULTS); for (int i = 0; i < rf.numFields(); i++) { auto qt = rf.fieldType(i); if (auto ft = qt.type()) { if (qt.kind() == QualifiedType::PARAM || qt.kind() == QualifiedType::TYPE) continue; - if (!Type::isPod(context, ft)) return false; + if (!pred(context, ft)) return false; } else { return false; } } + return true; +} + +static bool +compositeTypeIsPod(Context* context, const Type* t) { + using namespace resolution; + + if (auto cls = t->toClassType()) { + return !cls->decorator().isManaged(); + } + + auto ct = t->getCompositeType(); + if (!ct) return false; + + bool fieldsArePod = checkFieldsWithPredicate(context, t, Type::isPod); + if (!fieldsArePod) return false; + + // for tuple, this is enough; for other composite types, see if any user-defined + // methods are present. + if (t->isTupleType()) return true; + + const uast::AstNode* ast = nullptr; + if (auto id = ct->id()) ast = parsing::idToAst(context, std::move(id)); + if (auto tfs = tryResolveDeinit(context, ast, t)) { if (!tfs->isCompilerGenerated()) return false; } @@ -337,6 +355,61 @@ bool Type::isPod(Context* context, const Type* t) { return true; } +static bool const& isDefaultInitializableQuery(Context* context, const Type* t) { + QUERY_BEGIN(isDefaultInitializableQuery, context, t); + + bool result = true; + if (!t || t->isUnknownType() || t->isErroneousType()) { + result = false; + } else if (t->isBuiltinType()) { + result = t->genericity() == Type::CONCRETE; + } else if (auto at = t->toArrayType()) { + result = isDefaultInitializableQuery(context, at->eltType().type()); + } else if (t->isDomainType()) { + result = true; // production always returns true for domains. + } else if (t->isExternType()) { + // Currently extern records aren't initialized at all by default. + // But it's not necessarily reasonable to expect them to have + // initializers. See issue #7992 and preFold.cpp's setRecordDefaultValueFlags + // for FLAG_EXTERN. + result = true; + } else if (auto ct = t->toClassType()) { + result = ct->decorator().isNilable(); + } else if (t->isTupleType()) { + result = checkFieldsWithPredicate(context, t, Type::isDefaultInitializable); + } else if (t->isRecordLike()) { + // If the type doesn't have a user-defined initializer or is a tuple, check + // its fields. + auto fieldsDefaultInitializable = true; + if (resolution::needCompilerGeneratedMethod(context, t, USTR("init"), /* parenless */ false)) { + fieldsDefaultInitializable = checkFieldsWithPredicate(context, t, Type::isDefaultInitializable); + } + + if (!fieldsDefaultInitializable) { + result = false; + } else { + const uast::AstNode* ast = nullptr; + if (auto ct = t->getCompositeType()) { + if (auto id = ct->id()) { + ast = parsing::idToAst(context, std::move(id)); + } + } + + // note: production disallows default-init for generic fields like `var x;`, + // even if they are instantiated with a type that is default-initializable. + // But why? Seems like this is an implementation detail. Allow it in Dyno. + + result = resolution::tryResolveZeroArgInit(context, ast, t) != nullptr; + } + } + + return QUERY_END(result); +} + +bool Type::isDefaultInitializable(Context* context, const Type* t) { + return isDefaultInitializableQuery(context, t); +} + bool Type::needsInitDeinitCall(const Type* t) { if (t == nullptr || t->isUnknownType() || t->isErroneousType()) { // can't do anything with these diff --git a/frontend/test/resolution/CMakeLists.txt b/frontend/test/resolution/CMakeLists.txt index 54b90c3bc7d7..a20eb7778116 100644 --- a/frontend/test/resolution/CMakeLists.txt +++ b/frontend/test/resolution/CMakeLists.txt @@ -71,6 +71,7 @@ comp_unit_test(testParamFolding) comp_unit_test(testParamIf) comp_unit_test(testParams) comp_unit_test(testPoi) +comp_unit_test(testPrimHasDefaultValue) comp_unit_test(testProcThis) comp_unit_test(testPromotion) comp_unit_test(testRanges) diff --git a/frontend/test/resolution/testPrimHasDefaultValue.cpp b/frontend/test/resolution/testPrimHasDefaultValue.cpp new file mode 100644 index 000000000000..0a1d28b64b0c --- /dev/null +++ b/frontend/test/resolution/testPrimHasDefaultValue.cpp @@ -0,0 +1,87 @@ +/* + * Copyright 2025 Hewlett Packard Enterprise Development LP + * Other additional copyright holders may be indicated within. + * + * The entirety of this work is licensed under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "test-common.h" +#include "test-resolution.h" + +#include "chpl/parsing/parsing-queries.h" +#include "chpl/resolution/resolution-queries.h" +#include "chpl/resolution/scope-queries.h" +#include "chpl/types/all-types.h" +#include "chpl/uast/Identifier.h" +#include "chpl/uast/Module.h" +#include "chpl/uast/Record.h" +#include "chpl/uast/Variable.h" + +static void ensureExpectedDefaultValue(Context* context, const char* type, bool hasDefault, const char* prelude = "", bool skipErrors = false) { + ErrorGuard guard(context); + + auto primCallProgram = std::string(prelude) + "\nparam result = __primitive(\"type has default value\", " + type + ");"; + auto defaultInitProgram = std::string(prelude) + "\n" + "proc foo() { var x: " + type + "; }"; + + static int userFileCounter = 0; + auto filenameA = UniqueString::get(context, "A" + std::to_string(userFileCounter) + ".chpl"); + auto filenameB = UniqueString::get(context, "B" + std::to_string(userFileCounter++) + ".chpl"); + + setFileText(context, filenameA, primCallProgram); + setFileText(context, filenameB, defaultInitProgram); + + printf("parsing program:\n%s\n\n", primCallProgram.c_str()); + auto modA = parse(context, filenameA, UniqueString())[0]; + auto modAR = resolveModule(context, modA->id()); + auto result = modAR.byAst(findVariable(modA, "result")).type(); + ensureParamBool(result, hasDefault); + + if (skipErrors) { + printf("skipping checking for default-init errors\n"); + return; + } + printf("parsing program (%s):\n%s\n\n", + hasDefault ? "not expecting errors" : "expecting errors", + defaultInitProgram.c_str()); + auto modB = parse(context, filenameB, UniqueString())[0]; + std::ignore = resolveConcreteFunction(context, modB->stmt(modB->numStmts()-1)->id()); + if (hasDefault) { + assert(!guard.realizeErrors()); + } else { + assert(guard.realizeErrors()); + } +} + +int main() { + auto ctx = buildStdContext(); + ensureExpectedDefaultValue(ctx, "int", true); + ensureExpectedDefaultValue(ctx, "int(16)", true); + ensureExpectedDefaultValue(ctx, "string", true); + ensureExpectedDefaultValue(ctx, "(int, int)", true); + ensureExpectedDefaultValue(ctx, "owned C", false, "class C {}"); + ensureExpectedDefaultValue(ctx, "owned C?", true, "class C {}"); + ensureExpectedDefaultValue(ctx, "owned C", false, "class C { proc init(x: int) {} }"); + ensureExpectedDefaultValue(ctx, "owned C?", true, "class C { proc init(x: int) {} }"); + ensureExpectedDefaultValue(ctx, "R", true, "record R {}"); + ensureExpectedDefaultValue(ctx, "R", false, "record R { proc init(x: int) {} }"); + ensureExpectedDefaultValue(ctx, "(R, R)", true, "record R {}"); + ensureExpectedDefaultValue(ctx, "(R, R)", false, "record R { proc init(x: int) {} }", /*skipErrors=*/true); + + // seems like we can't build default-initializers for records with default-initializable fields + // in all cases. Skip these tests as a consequence. + // + // ensureExpectedDefaultValue(ctx, "Wrap(R)", true, "record Wrap { var field; } record R {}"); + // ensureExpectedDefaultValue(ctx, "Wrap(R)", false, "record Wrap { var field; } record R { proc init(x: int) {} }"); +} diff --git a/frontend/test/resolution/testTypePropertyPrimitives.cpp b/frontend/test/resolution/testTypePropertyPrimitives.cpp index 74e5bbe28483..00c1c97f1c03 100644 --- a/frontend/test/resolution/testTypePropertyPrimitives.cpp +++ b/frontend/test/resolution/testTypePropertyPrimitives.cpp @@ -52,7 +52,7 @@ struct Test { }; std::string testName; - bool isChplHomeRequired = false; + bool useStdContext = false; std::string prelude; chpl::uast::primtags::PrimitiveTag primitive; std::vector calls; @@ -84,27 +84,13 @@ assertParamStringMatch(chpl::types::QualifiedType qt, std::string str, } } -static void testPrimitive(const Test& tpg) { - Context::Configuration config; - if (tpg.isChplHomeRequired) { - if (const char* chplHomeEnv = getenv("CHPL_HOME")) { - config.chplHome = chplHomeEnv; - } else { - std::cout << "CHPL_HOME must be set!" << std::endl; - exit(1); - } - } - Context ctx(config); - Context* context = &ctx; +static void testPrimitive(const Test& tpg, int unrelatedErrors = 0) { + Context* context = tpg.useStdContext ? buildStdContext() : new Context(); ErrorGuard guard(context); - if (tpg.isChplHomeRequired) { - setupModuleSearchPaths(context, false, false, {}, {}); - } - std::stringstream ps; int counter = 0; - int expectedErrorCount = 0; + int expectedErrorCount = unrelatedErrors; std::vector variables; const auto tagStr = chpl::uast::primtags::primTagToName(tpg.primitive); @@ -168,7 +154,7 @@ static void testPrimitive(const Test& tpg) { static void test0() { Test tpg { /* testName */ __FUNCTION__, - /* isChplHomeRequired */ false, + /* useStdContext */ false, /* prelude */ R"""( record r1 { var x: int; } record r2 { type T; var x: T; } @@ -235,7 +221,7 @@ static void test0() { static void test1() { Test tpg { /* testName */ __FUNCTION__, - /* isChplHomeRequired */ false, + /* useStdContext */ false, /* prelude */ R"""( record r1 { var x: int; } class c1 { var x: int; } @@ -257,7 +243,7 @@ static void test1() { static void test2() { Test tpg { /* testName */ __FUNCTION__, - /* isChplHomeRequired */ false, + /* useStdContext */ false, /* prelude */ R"""( record r1 { var x: int; } class c1 { var x: int; } @@ -278,7 +264,7 @@ static void test2() { static void test3() { Test tpg { /* testName */ __FUNCTION__, - /* isChplHomeRequired */ false, + /* useStdContext */ false, /* prelude */ R"""( record r1 { var x: int; } class c1 { var x: int; } @@ -300,7 +286,7 @@ static void test3() { static void test4() { Test tpg { /* testName */ __FUNCTION__, - /* isChplHomeRequired */ false, + /* useStdContext */ false, /* prelude */ R"""( record r1 { var x: int; } class c1 { var x: int; } @@ -322,7 +308,7 @@ static void test4() { static void test5() { Test tpg { /* testName */ __FUNCTION__, - /* isChplHomeRequired */ false, + /* useStdContext */ false, /* prelude */ R"""( record r1 { var x: int; } class c1 { var x: int; } @@ -347,7 +333,7 @@ static void test5() { static void test6() { Test tpg { .testName=__FUNCTION__, - .isChplHomeRequired= true, + .useStdContext= true, .prelude=R"""( record r1 { var x: int; } )""", @@ -367,7 +353,7 @@ static void test6() { static void test7() { Test tpg { /* testName */ __FUNCTION__, - /* isChplHomeRequired */ false, + /* useStdContext */ false, /* prelude */ R"""( record r1 { var x: int; } class c1 { var x: int; } @@ -391,7 +377,7 @@ static void test7() { static void test8() { Test tpg { /* testName */ __FUNCTION__, - /* isChplHomeRequired */ false, + /* useStdContext */ false, /* prelude */ R"""( record r1 { var x: int; } class c1 { var x: int; } @@ -437,7 +423,7 @@ static void test8() { static void test9() { Test tpg { /* testName */ __FUNCTION__, - /* isChplHomeRequired */ false, + /* useStdContext */ false, /* prelude */ R"""( record r1 { var x: int; } class c1 { var x: int; } @@ -467,7 +453,7 @@ static void test9() { static void test10() { Test tpg { /* testName */ __FUNCTION__, - /* isChplHomeRequired */ false, + /* useStdContext */ false, /* prelude */ R"""( record r1 { var x: int; } record r2 { type T; var x: T; } @@ -520,7 +506,7 @@ static void test10() { static void test11() { Test tpg { /* testName */ __FUNCTION__, - /* isChplHomeRequired */ false, + /* useStdContext */ false, /* prelude */ R"""( record r1 { var x: int; } record r2 { type T; var x: T; } @@ -570,7 +556,7 @@ static void test11() { static void test12() { Test tpg { /* testName */ __FUNCTION__, - /* isChplHomeRequired */ false, // TODO: True... + /* useStdContext */ false, // TODO: True... /* prelude */ R"""( pragma "ignore noinit" record r1 {} @@ -648,7 +634,7 @@ static void test12() { static void test13() { Test tpg { /* testName */ __FUNCTION__, - /* isChplHomeRequired */ false, + /* useStdContext */ false, /* prelude */ R"""( extern type foo; extern record bar {} @@ -664,6 +650,57 @@ static void test13() { testPrimitive(tpg); } +static void test14() { + Test tpg { + /* testName */ __FUNCTION__, + /* useStdContext */ true, + /* prelude */ R"""( + class Foo {} + record bar {} + enum color {blue, red} + union baz { var f : owned Foo; } + )""", + /* primitive */ chpl::uast::primtags::PRIM_HAS_DEFAULT_VALUE, + /* calls */ { + /* primitive types */ + { {"bool"}, Test::TRUE }, + { {"int"}, Test::TRUE }, + { {"int(64)"}, Test::TRUE }, + { {"int(32)"}, Test::TRUE }, + { {"uint"}, Test::TRUE }, + { {"real"}, Test::TRUE }, + { {"imag"}, Test::TRUE }, + { {"complex"}, Test::TRUE }, + /* record-like builtin types */ + { {"string"}, Test::TRUE }, + { {"bytes"}, Test::TRUE }, + { {"range"}, Test::TRUE }, + { {"sync int"}, Test::TRUE }, + { {"atomic int"}, Test::TRUE }, + /* generic builtin types */ + { {"integral"}, Test::FALSE }, + { {"numeric"}, Test::FALSE }, + { {"enum"}, Test::FALSE }, + { {"record"}, Test::FALSE }, + { {"class"}, Test::FALSE }, + { {"class?"}, Test::TRUE }, + { {"shared"}, Test::FALSE }, + { {"shared class"}, Test::FALSE }, + { {"shared class?"}, Test::TRUE }, + /* user-defined types */ + { {"bar"}, Test::TRUE }, + { {"owned Foo"}, Test::FALSE }, + { {"owned Foo?"}, Test::TRUE }, + { {"color"}, Test::TRUE }, + { {"(int, bool)"}, Test::TRUE }, + { {"(int, color)"}, Test::TRUE }, + { {"(int, owned Foo)"}, Test::FALSE }, + { {"baz"}, Test::FALSE }, + }, + }; + testPrimitive(tpg); +} + int main() { test0(); test1(); @@ -679,4 +716,5 @@ int main() { test11(); test12(); test13(); + test14(); }