diff --git a/compiler/AST/wellknown.cpp b/compiler/AST/wellknown.cpp index 4fc2033d255..169a5d7f624 100644 --- a/compiler/AST/wellknown.cpp +++ b/compiler/AST/wellknown.cpp @@ -55,6 +55,7 @@ AggregateType* dtTuple; // these are only used when the dyno resolver is active AggregateType* dtCPointer; AggregateType* dtCPointerConst; +AggregateType* dtHeapBuffer; Type* dt_c_int; Type* dt_c_uint; @@ -201,6 +202,7 @@ static WellKnownAggregateTypeNeededEarly sWellKnownAggregateTypesNeededEarly[]= { "_tuple", nullptr, &dtTuple, false }, { "c_ptr", "c_ptr", &dtCPointer, true }, { "c_ptrConst", "c_ptrConst", &dtCPointerConst, true }, + { "_ddata", "_ddata", &dtHeapBuffer, true }, }; struct WellKnownType diff --git a/compiler/include/wellknown.h b/compiler/include/wellknown.h index d734f737b3d..1ad31eb8e72 100644 --- a/compiler/include/wellknown.h +++ b/compiler/include/wellknown.h @@ -74,6 +74,7 @@ extern AggregateType* dtTuple; // these are only used when the dyno resolver is active extern AggregateType* dtCPointer; extern AggregateType* dtCPointerConst; +extern AggregateType* dtHeapBuffer; extern Type* dt_c_int; extern Type* dt_c_uint; diff --git a/compiler/passes/convert-uast.cpp b/compiler/passes/convert-uast.cpp index 93efea3b390..40bd72aa87d 100644 --- a/compiler/passes/convert-uast.cpp +++ b/compiler/passes/convert-uast.cpp @@ -320,7 +320,7 @@ struct Converter { // type conversion helpers Type* helpConvertType(types::QualifiedType qt); Type* convertClassType(const types::QualifiedType qt); - Type* convertCPtrType(const types::CPtrType* t); + Type* convertPtrType(const types::PtrType* t); Type* convertEnumType(const types::QualifiedType qt); Type* convertExternType(const types::QualifiedType qt); Type* convertFunctionType(const types::QualifiedType qt); @@ -4584,7 +4584,8 @@ Type* Converter::helpConvertType(types::QualifiedType qt) { case typetags::IntType: return convertIntType(qt); case typetags::RealType: return convertRealType(qt); case typetags::UintType: return convertUintType(qt); - case typetags::CPtrType: return convertCPtrType(t->toCPtrType()); + case typetags::CPtrType: return convertPtrType(t->toPtrType()); + case typetags::HeapBufferType: return convertPtrType(t->toPtrType()); // implementation detail tags (should not be reachable) case typetags::START_ManageableType: @@ -4599,6 +4600,8 @@ Type* Converter::helpConvertType(types::QualifiedType qt) { case typetags::END_PrimitiveType: case typetags::START_IteratorType: case typetags::END_IteratorType: + case typetags::START_PtrType: + case typetags::END_PtrType: case typetags::NUM_TYPE_TAGS: INT_FATAL("should not be reachable"); return dtUnknown; @@ -4610,11 +4613,17 @@ Type* Converter::helpConvertType(types::QualifiedType qt) { return nullptr; } -Type* Converter::convertCPtrType(const types::CPtrType* t) { - // find the C pointer type to instantiate - AggregateType* base = t->isConst() ? dtCPointerConst : dtCPointer; +Type* Converter::convertPtrType(const types::PtrType* t) { + // find the pointer type to instantiate + AggregateType* base = nullptr; + if (auto ct = t->toCPtrType()) { + base = ct->isConst() ? dtCPointerConst : dtCPointer; + } else { + INT_ASSERT(t->toHeapBufferType()); + base = dtHeapBuffer; + } - // handle 'c_ptr' and 'c_ptrConst' without an element type + // handle ptr without an element type if (t->eltType() == nullptr) { return base; } @@ -4622,7 +4631,7 @@ Type* Converter::convertCPtrType(const types::CPtrType* t) { auto qt = types::QualifiedType(types::QualifiedType::TYPE, t); if (base->numFields() == 0) { - // the proper AST for CPointer hasn't been created yet, + // the proper AST for the pointer type hasn't been created yet, // and we need it to proceed, so return a temporary conversion symbol. Type* t = new TemporaryConversionType(qt); return t; diff --git a/frontend/include/chpl/framework/all-global-strings.h b/frontend/include/chpl/framework/all-global-strings.h index 08ec0ef01d7..850e1a07bd1 100644 --- a/frontend/include/chpl/framework/all-global-strings.h +++ b/frontend/include/chpl/framework/all-global-strings.h @@ -43,6 +43,7 @@ X(c_ptr , "c_ptr") X(c_ptrConst , "c_ptrConst") X(c_char , "c_char") X(class_ , "class") +X(ddata_ , "_ddata") X(defaultDist , "defaultDist") X(deinit , "deinit") X(deserialize , "deserialize") diff --git a/frontend/include/chpl/types/CPtrType.h b/frontend/include/chpl/types/CPtrType.h index 7d9b9532021..c45df14e940 100644 --- a/frontend/include/chpl/types/CPtrType.h +++ b/frontend/include/chpl/types/CPtrType.h @@ -20,59 +20,35 @@ #ifndef CHPL_TYPES_CPTR_TYPE_H #define CHPL_TYPES_CPTR_TYPE_H +#include "chpl/types/PtrType.h" #include "chpl/types/Type.h" #include "chpl/types/QualifiedType.h" namespace chpl { namespace types { -class CPtrType final : public Type { +class CPtrType final : public PtrType { private: - const CPtrType* instantiatedFrom_; - const Type* eltType_; const bool isConst_ = false; - CPtrType(const CPtrType* instantiatedFrom, - const Type* eltType, + CPtrType(const CPtrType* instantiatedFrom, const Type* eltType, bool isConst = false) - : Type(typetags::CPtrType), instantiatedFrom_(instantiatedFrom), - eltType_(eltType), isConst_(isConst) { - // not an instantiation -> eltType_ should be empty - CHPL_ASSERT(instantiatedFrom_ != nullptr || eltType_ == nullptr); - // is an instantiation -> eltType should not be empty - CHPL_ASSERT(instantiatedFrom_ == nullptr || eltType_ != nullptr); - } + : PtrType(typetags::CPtrType, instantiatedFrom, eltType), + isConst_(isConst) {} bool contentsMatchInner(const Type* other) const override { - auto rhs = (CPtrType*) other; + auto rhs = (CPtrType*)other; return instantiatedFrom_ == rhs->instantiatedFrom_ && - eltType_ == rhs->eltType_ && - isConst_ == rhs->isConst_; + eltType_ == rhs->eltType_ && isConst_ == rhs->isConst_; } void markUniqueStringsInner(Context* context) const override {} - Genericity genericity() const override { - if (!eltType_) return GENERIC; - return eltType_->genericity(); - } - static const owned& getCPtrType(Context* context, const CPtrType* instantiatedFrom, - const Type* eltType, - bool isConst); - - const CPtrType* instantiatedFromCPtrType() const { - // at present, only expecting a single level of instantiated-from. - CHPL_ASSERT(instantiatedFrom_ == nullptr || - instantiatedFrom_->instantiatedFrom_ == nullptr); - return instantiatedFrom_; - } - - bool isEltTypeInstantiationOf(Context* context, const CPtrType* other) const; + const Type* eltType, bool isConst); public: - static const CPtrType* get(Context* context); static const CPtrType* get(Context* context, const Type* eltType); static const CPtrType* getCVoidPtrType(Context* context); @@ -82,27 +58,13 @@ class CPtrType final : public Type { static const ID& getId(Context* context); static const ID& getConstId(Context* context); - const ID& id(Context* context) const { + const ID& id(Context* context) const override { return isConst() ? getConstId(context) : getId(context); } - const Type* eltType() const { - return eltType_; - } - const CPtrType* withoutConst(Context* context) const; - bool isConst() const { - return isConst_; - } - - bool isVoidPtr() const { - if (eltType_ == nullptr) return false; - return eltType_->isVoidType(); - } - - bool isInstantiationOf(Context* context, - const CPtrType* genericType) const; + bool isConst() const { return isConst_; } virtual void stringify(std::ostream& ss, chpl::StringifyKind stringKind) const override; diff --git a/frontend/include/chpl/types/HeapBufferType.h b/frontend/include/chpl/types/HeapBufferType.h new file mode 100644 index 00000000000..fbffd57a051 --- /dev/null +++ b/frontend/include/chpl/types/HeapBufferType.h @@ -0,0 +1,62 @@ +/* + * Copyright 2021-2024 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. + */ + +#ifndef CHPL_TYPES_HEAPBUFFER_TYPE_H +#define CHPL_TYPES_HEAPBUFFER_TYPE_H + +#include "chpl/types/PtrType.h" +#include "chpl/types/Type.h" + +namespace chpl { +namespace types { + +class HeapBufferType final : public PtrType { + private: + HeapBufferType(const HeapBufferType* instantiatedFrom, const Type* eltType) + : PtrType(typetags::HeapBufferType, instantiatedFrom, eltType) {} + + bool contentsMatchInner(const Type* other) const override { + auto rhs = (HeapBufferType*)other; + return instantiatedFrom_ == rhs->instantiatedFrom_ && + eltType_ == rhs->eltType_; + } + + void markUniqueStringsInner(Context* context) const override {} + + static const owned& getHeapBufferType( + Context* context, const HeapBufferType* instantiatedFrom, + const Type* eltType); + + public: + static const HeapBufferType* get(Context* context); + static const HeapBufferType* get(Context* context, const Type* eltType); + static const ID& getId(Context* context); + + const ID& id(Context* context) const override { + return getId(context); + } + + virtual void stringify(std::ostream& ss, + chpl::StringifyKind stringKind) const override; +}; + +} // end namespace types +} // end namespace chpl + +#endif diff --git a/frontend/include/chpl/types/PtrType.h b/frontend/include/chpl/types/PtrType.h new file mode 100644 index 00000000000..8411b796cbd --- /dev/null +++ b/frontend/include/chpl/types/PtrType.h @@ -0,0 +1,80 @@ +/* + * Copyright 2021-2024 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. + */ + +#ifndef CHPL_TYPES_PTR_TYPE_H +#define CHPL_TYPES_PTR_TYPE_H + +#include "chpl/types/Type.h" +#include "chpl/types/QualifiedType.h" + +namespace chpl { +namespace types { + +class PtrType : public Type { + protected: + const PtrType* instantiatedFrom_; + const Type* eltType_; + + PtrType(typetags::TypeTag tag, const PtrType* instantiatedFrom, + const Type* eltType) + : Type(tag), instantiatedFrom_(instantiatedFrom), eltType_(eltType) { + // not an instantiation -> eltType_ should be empty + CHPL_ASSERT(instantiatedFrom_ != nullptr || eltType_ == nullptr); + // is an instantiation -> eltType should not be empty + CHPL_ASSERT(instantiatedFrom_ == nullptr || eltType_ != nullptr); + + // check instantiated only from same type of object + CHPL_ASSERT(instantiatedFrom_ == nullptr || + instantiatedFrom_->tag() == tag); + } + + Genericity genericity() const override { + if (!eltType_) return GENERIC; + return eltType_->genericity(); + } + + bool isEltTypeInstantiationOf(Context* context, const PtrType* other) const; + + const PtrType* instantiatedFromPtrType() const { + // at present, only expecting a single level of instantiated-from. + CHPL_ASSERT(instantiatedFrom_ == nullptr || + instantiatedFrom_->instantiatedFrom_ == nullptr); + return instantiatedFrom_; + } + + public: + virtual const ID& id(Context* context) const = 0; + + const Type* eltType() const { + return eltType_; + } + + bool isVoidPtr() const { + if (eltType_ == nullptr) return false; + return eltType_->isVoidType(); + } + + bool isInstantiationOf(Context* context, + const PtrType* genericType) const; +}; + +} // end namespace types +} // end namespace chpl + +#endif diff --git a/frontend/include/chpl/types/Type.h b/frontend/include/chpl/types/Type.h index e6ce8e2e4d9..31bcaa26dbf 100644 --- a/frontend/include/chpl/types/Type.h +++ b/frontend/include/chpl/types/Type.h @@ -230,8 +230,8 @@ class Type { } /** returns true for a type that is a kind of pointer */ - bool isPtrType() const { - return isClassType() || isCFnPtrType() || isCVoidPtrType() || isCPtrType(); + bool isAnyPtrType() const { + return isClassType() || isCFnPtrType() || isCVoidPtrType() || isPtrType(); } /** returns true for a pointer type that can store nil */ diff --git a/frontend/include/chpl/types/all-types.h b/frontend/include/chpl/types/all-types.h index a2b17759cd8..9f7e61a31d1 100644 --- a/frontend/include/chpl/types/all-types.h +++ b/frontend/include/chpl/types/all-types.h @@ -34,6 +34,7 @@ #include "chpl/types/ErroneousType.h" #include "chpl/types/ExternType.h" #include "chpl/types/FnIteratorType.h" +#include "chpl/types/HeapBufferType.h" #include "chpl/types/ImagType.h" #include "chpl/types/IntType.h" #include "chpl/types/IteratorType.h" @@ -42,6 +43,7 @@ #include "chpl/types/NothingType.h" #include "chpl/types/Param.h" #include "chpl/types/PrimitiveType.h" +#include "chpl/types/PtrType.h" #include "chpl/types/PromotionIteratorType.h" #include "chpl/types/QualifiedType.h" #include "chpl/types/RealType.h" diff --git a/frontend/include/chpl/types/type-classes-list.h b/frontend/include/chpl/types/type-classes-list.h index b876d7249de..91c3d347b6f 100644 --- a/frontend/include/chpl/types/type-classes-list.h +++ b/frontend/include/chpl/types/type-classes-list.h @@ -46,7 +46,11 @@ TYPE_NODE(NilType) TYPE_NODE(NothingType) TYPE_NODE(UnknownType) TYPE_NODE(VoidType) -TYPE_NODE(CPtrType) + +TYPE_BEGIN_SUBCLASSES(PtrType) + TYPE_NODE(CPtrType) + TYPE_NODE(HeapBufferType) +TYPE_END_SUBCLASSES(PtrType) TYPE_BEGIN_SUBCLASSES(IteratorType) TYPE_NODE(LoopExprIteratorType) @@ -56,11 +60,9 @@ TYPE_END_SUBCLASSES(IteratorType) // TODO: -// migrate BytesType / StringType to something backed by the modules -// (if the modules are parsed) and also do the same for array, domain, -// distribution. -// -// c_ptr +// migrate array and distribution to something backed by the modules +// (if the modules are parsed) + // c_array TYPE_BEGIN_SUBCLASSES(BuiltinType) diff --git a/frontend/lib/resolution/Resolver.cpp b/frontend/lib/resolution/Resolver.cpp index c07611f8e58..a3a376eeb92 100644 --- a/frontend/lib/resolution/Resolver.cpp +++ b/frontend/lib/resolution/Resolver.cpp @@ -890,12 +890,13 @@ static bool isCallToIntEtc(const AstNode* formalTypeExpr) { return false; } -static bool isCallToCPtr(const AstNode* formalTypeExpr) { +static bool isCallToPtr(const AstNode* formalTypeExpr) { if (auto call = formalTypeExpr->toFnCall()) { if (auto calledAst = call->calledExpression()) { if (auto calledIdent = calledAst->toIdentifier()) { UniqueString n = calledIdent->name(); - if (n == USTR("c_ptr") || n == USTR("c_ptrConst")) { + if (n == USTR("c_ptr") || n == USTR("c_ptrConst") || + n == USTR("_ddata")) { return true; } } @@ -979,12 +980,12 @@ void Resolver::resolveTypeQueries(const AstNode* formalTypeExpr, } } } - } else if (isCallToCPtr(formalTypeExpr)) { + } else if (isCallToPtr(formalTypeExpr)) { // If it is e.g. c_ptr(TypeQuery), resolve the type query to the eltType // Set the type that we know (since it was passed in) if (call->numActuals() == 1) { if (auto tq = call->actual(0)->toTypeQuery()) { - if (auto pt = actualTypePtr->toCPtrType()) { + if (auto pt = actualTypePtr->toPtrType()) { ResolvedExpression& resolvedElt = byPostorder.byAst(tq); if (isNonStarVarArg) { varArgTypeQueryError(context, call->actual(0), resolvedElt); @@ -2445,12 +2446,14 @@ QualifiedType Resolver::typeForId(const ID& id, bool localGenericToUnknown) { return QualifiedType(kind, type); } - // Intercept the standard library `c_ptr` and `c_ptrConst` and turn them into - // the builtin type. + // Intercept the standard library `c_ptr`, `c_ptrConst`, and `_ddata`, and + // turn them into the appropriate builtin type. if (id == CPtrType::getId(context)) { return QualifiedType(QualifiedType::TYPE, CPtrType::get(context)); } else if (id == CPtrType::getConstId(context)) { return QualifiedType(QualifiedType::TYPE, CPtrType::getConst(context)); + } else if (id == HeapBufferType::getId(context)) { + return QualifiedType(QualifiedType::TYPE, HeapBufferType::get(context)); } // if the id is contained within this symbol, diff --git a/frontend/lib/resolution/can-pass.cpp b/frontend/lib/resolution/can-pass.cpp index d910265d411..247837cbe9f 100644 --- a/frontend/lib/resolution/can-pass.cpp +++ b/frontend/lib/resolution/can-pass.cpp @@ -943,22 +943,26 @@ CanPassResult CanPassResult::canInstantiate(Context* context, return instantiate(); } } - } else if (auto actualPt = actualT->toCPtrType()) { - if (auto formalPt = formalT->toCPtrType()) { + } else if (auto actualPt = actualT->toPtrType()) { + if (auto formalPt = formalT->toPtrType()) { // Check first if they're direct instantiations (c_ptr(int(?w)) <- c_ptr(int)). if (actualPt->isInstantiationOf(context, formalPt)) { return instantiate(); } - // Instantiation might still be possible, together with a coercion, if - // the formal is const but the actual isn't. - formalPt = formalPt->withoutConst(context); - - if (actualPt->isInstantiationOf(context, formalPt)) { - return CanPassResult(/* no fail reason, passes */ {}, - /* instantiates */ true, - /* promotes */ false, - /* converts */ ConversionKind::OTHER); + if (auto actualCPt = actualPt->toCPtrType()) { + if (auto formalCPt = formalPt->toCPtrType()) { + // Instantiation might still be possible, together with a coercion, if + // the formal is const but the actual isn't. + formalCPt = formalCPt->withoutConst(context); + + if (actualCPt->isInstantiationOf(context, formalCPt)) { + return CanPassResult(/* no fail reason, passes */ {}, + /* instantiates */ true, + /* promotes */ false, + /* converts */ ConversionKind::OTHER); + } + } } } } diff --git a/frontend/lib/resolution/default-functions.cpp b/frontend/lib/resolution/default-functions.cpp index 96f188ede65..2b33711c077 100644 --- a/frontend/lib/resolution/default-functions.cpp +++ b/frontend/lib/resolution/default-functions.cpp @@ -146,9 +146,11 @@ needCompilerGeneratedMethod(Context* context, const Type* type, if (name == "domain" || name == "eltType") { return true; } - } else if (type->isCPtrType()) { + } else if (type->isPtrType()) { if (name == "eltType") { return true; + } else if (type->isHeapBufferType() && name == "this") { + return true; } } else if (type->isEnumType()) { if (name == "size") { @@ -973,11 +975,11 @@ generateRecordComparison(Context* context, const CompositeType* lhsType) { } static const TypedFnSignature* -generateCPtrMethod(Context* context, QualifiedType receiverType, +generatePtrMethod(Context* context, QualifiedType receiverType, UniqueString name) { - // Build a basic function signature for methods on a cptr + // Build a basic function signature for methods on a PtrType // TODO: we should really have a way to just set the return type here - const CPtrType* cpt = receiverType.type()->toCPtrType(); + const PtrType* pt = receiverType.type()->toPtrType(); const TypedFnSignature* result = nullptr; std::vector formals; std::vector formalTypes; @@ -986,13 +988,20 @@ generateCPtrMethod(Context* context, QualifiedType receiverType, UntypedFnSignature::FormalDetail(USTR("this"), UntypedFnSignature::DK_NO_DEFAULT, nullptr)); - // Allow calling 'eltType' on either a type or value auto qual = receiverType.isType() ? QualifiedType::TYPE : QualifiedType::CONST_REF; - formalTypes.push_back(QualifiedType(qual, cpt)); + formalTypes.push_back(QualifiedType(qual, pt)); + + if (name == "this") { + formals.push_back(UntypedFnSignature::FormalDetail( + UniqueString::get(context, "i"), UntypedFnSignature::DK_NO_DEFAULT, + nullptr)); + formalTypes.push_back( + QualifiedType(QualifiedType::VAR, AnyIntegralType::get(context))); + } auto ufs = UntypedFnSignature::get(context, - /*id*/ cpt->id(context), + /*id*/ pt->id(context), /*name*/ name, /*isMethod*/ true, /*isTypeConstructor*/ false, @@ -1068,7 +1077,7 @@ getCompilerGeneratedMethodQuery(Context* context, QualifiedType receiverType, if (needCompilerGeneratedMethod(context, type, name, parenless)) { auto compType = type->getCompositeType(); - CHPL_ASSERT(compType || type->isCPtrType() || type->isEnumType()); + CHPL_ASSERT(compType || type->isPtrType() || type->isEnumType()); if (name == USTR("init")) { result = generateInitSignature(context, compType); @@ -1092,8 +1101,8 @@ getCompilerGeneratedMethodQuery(Context* context, QualifiedType receiverType, } else { CHPL_UNIMPL("record method not implemented yet!"); } - } else if (type->isCPtrType()) { - result = generateCPtrMethod(context, receiverType, name); + } else if (type->isPtrType()) { + result = generatePtrMethod(context, receiverType, name); } else if (auto enumType = type->toEnumType()) { result = generateEnumMethod(context, enumType, name); } else { @@ -1203,12 +1212,12 @@ generateCastToEnum(Context* context, const TypedFnSignature* getCompilerGeneratedMethod(Context* context, const QualifiedType receiverType, UniqueString name, bool parenless) { - // Normalize recieverType to allow TYPE methods on c_ptr, and to otherwise - // use the VAR Kind. The Param* value is also stripped away to reduce - // queries. + // Normalize recieverType to allow TYPE methods on c_ptr and _ddata, and to + // otherwise use the VAR Kind. The Param* value is also stripped away to + // reduce queries. auto qt = receiverType; - bool isCPtr = qt.hasTypePtr() ? qt.type()->isCPtrType() : false; - if (!(qt.isType() && isCPtr)) { + bool isPtr = qt.hasTypePtr() ? qt.type()->isPtrType() : false; + if (!(qt.isType() && isPtr)) { qt = QualifiedType(QualifiedType::VAR, qt.type()); } return getCompilerGeneratedMethodQuery(context, qt, name, parenless); diff --git a/frontend/lib/resolution/intents.cpp b/frontend/lib/resolution/intents.cpp index 37aefb14c37..eb387233bf9 100644 --- a/frontend/lib/resolution/intents.cpp +++ b/frontend/lib/resolution/intents.cpp @@ -40,7 +40,7 @@ static QualifiedType::Kind constIntentForType(const Type* t) { if (t->isPrimitiveType() || t->isEnumType() || t->isExternType() || t->isOpaqueType() || t->isTaskIdType() || t->isNilType() || t->isCStringType() || t->isCVoidPtrType() || t->isCFnPtrType() || - t->isNothingType() || t->isVoidType() || t->isCPtrType()) + t->isNothingType() || t->isVoidType() || t->isPtrType()) return QualifiedType::CONST_IN; if (t->isStringType() || t->isBytesType() || @@ -72,7 +72,7 @@ static QualifiedType::Kind defaultIntentForType(const Type* t, if (t->isPrimitiveType() || t->isEnumType() || t->isExternType() || t->isOpaqueType() || t->isTaskIdType() || t->isNilType() || - t->isCStringType() || t->isCVoidPtrType() || t->isCPtrType() || + t->isCStringType() || t->isCVoidPtrType() || t->isPtrType() || t->isCFnPtrType() || t->isNothingType() || t->isVoidType()) return QualifiedType::CONST_IN; diff --git a/frontend/lib/resolution/resolution-queries.cpp b/frontend/lib/resolution/resolution-queries.cpp index 107fe688e5e..800375879a5 100644 --- a/frontend/lib/resolution/resolution-queries.cpp +++ b/frontend/lib/resolution/resolution-queries.cpp @@ -1254,7 +1254,7 @@ Type::Genericity getTypeGenericityIgnoring(Context* context, const Type* t, if (t->isUnknownType()) return Type::MAYBE_GENERIC; - if (auto pt = t->toCPtrType()) { + if (auto pt = t->toPtrType()) { // Mimics the fields logic: if any field is non-concrete, the whole // type is generic. Logically, the c_ptr has a single field, the element // type. @@ -3573,6 +3573,65 @@ static const Type* getCPtrType(Context* context, } } +static const Type* getHeapBufferType(Context* context, + const AstNode* astForErr, + const CallInfo& ci) { + UniqueString name = ci.name(); + + auto called = ci.calledType(); + if (!(called.hasTypePtr() && called.type()->isHeapBufferType())) { + return nullptr; + } + if (name != USTR("_ddata")) { + return nullptr; + } + + bool useGenericType = false; + // There should be 0 or 1 actuals depending on if it is ? + if (ci.hasQuestionArg()) { + if (ci.numActuals() != 0) { + context->error(astForErr, "invalid %s type construction", name.c_str()); + return ErroneousType::get(context); + } + useGenericType = true; + } else { + if (ci.numActuals() != 1) { + context->error(astForErr,"invalid %s type construction", name.c_str()); + return ErroneousType::get(context); + } + + QualifiedType qt = ci.actual(0).type(); + if (qt.type() && qt.type()->isAnyType()) { + useGenericType = true; + } + } + if (useGenericType) { + return HeapBufferType::get(context); + } + + QualifiedType qt; + CHPL_ASSERT(ci.numActuals() > 0); + qt = ci.actual(0).type(); + + const Type* t = qt.type(); + if (t == nullptr) { + // Details not yet known so return UnknownType + return UnknownType::get(context); + } else if (t->isUnknownType() || t->isErroneousType()) { + // Just propagate the Unknown / Erroneous type + // without raising any errors + return t; + } + + if (!qt.isType()) { + // raise an error b/c of type mismatch + context->error(astForErr, "invalid %s type construction", name.c_str()); + return ErroneousType::get(context); + } else { + return HeapBufferType::get(context, t); + } +} + static const Type* convertClassTypeToNilable(Context* context, const Type* t) { const ClassType* ct = nullptr; @@ -3633,6 +3692,10 @@ static const Type* resolveBuiltinTypeCtor(Context* context, return t; } + if (auto t = getHeapBufferType(context, astForErr, ci)) { + return t; + } + return nullptr; } diff --git a/frontend/lib/resolution/resolution-types.cpp b/frontend/lib/resolution/resolution-types.cpp index b0abde2db5a..5704b2cc391 100644 --- a/frontend/lib/resolution/resolution-types.cpp +++ b/frontend/lib/resolution/resolution-types.cpp @@ -1536,7 +1536,7 @@ ReceiverScopeTypedHelper::methodLookupForType(Context* context, QualifiedType type) const { if (const Type* typePtr = type.type()) { if (typePtr->getCompositeType() || - typePtr->isCPtrType() || + typePtr->isPtrType() || typePtr->isExternType()) { // OK, it's a type that we need to gather receiver scopes for diff --git a/frontend/lib/resolution/return-type-inference.cpp b/frontend/lib/resolution/return-type-inference.cpp index 2781c698b4d..316c93456e8 100644 --- a/frontend/lib/resolution/return-type-inference.cpp +++ b/frontend/lib/resolution/return-type-inference.cpp @@ -985,9 +985,16 @@ static bool helpComputeCompilerGeneratedReturnType(Context* context, CHPL_ASSERT(false && "unhandled compiler-generated array method"); } return true; - } else if (untyped->isMethod() && sig->formalType(0).type()->isCPtrType() && untyped->name() == "eltType") { - auto cpt = sig->formalType(0).type()->toCPtrType(); - result = QualifiedType(QualifiedType::TYPE, cpt->eltType()); + } else if (untyped->isMethod() && sig->formalType(0).type()->isPtrType() && + untyped->name() == "eltType") { + auto pt = sig->formalType(0).type()->toPtrType(); + result = QualifiedType(QualifiedType::TYPE, pt->eltType()); + return true; + } else if (untyped->isMethod() && + sig->formalType(0).type()->isHeapBufferType() && + untyped->name() == "this") { + auto pt = sig->formalType(0).type()->toHeapBufferType(); + result = QualifiedType(QualifiedType::REF, pt->eltType()); return true; } else if (untyped->isMethod() && sig->formalType(0).type()->isEnumType()) { auto enumType = sig->formalType(0).type()->toEnumType(); diff --git a/frontend/lib/resolution/scope-queries.cpp b/frontend/lib/resolution/scope-queries.cpp index 3f15bdf94ad..c59cbb7e10b 100644 --- a/frontend/lib/resolution/scope-queries.cpp +++ b/frontend/lib/resolution/scope-queries.cpp @@ -190,6 +190,9 @@ struct GatherDecls { // TODO: can we remove this at some point when TupleType becomes close // enough to the _tuple record? skip = true; + } else if (d->isClass() && d->name() == "_ddata") { + // ditto for _ddata + skip = true; } else if (d->name() == "eltType" && atFieldLevel && tagParent == asttags::Class && (d->id().symbolPath().startsWith("CTypes.c_ptr") || diff --git a/frontend/lib/types/CMakeLists.txt b/frontend/lib/types/CMakeLists.txt index 5c6924dc1d6..ff8ff22f608 100644 --- a/frontend/lib/types/CMakeLists.txt +++ b/frontend/lib/types/CMakeLists.txt @@ -35,6 +35,7 @@ target_sources(ChplFrontend-obj ErroneousType.cpp ExternType.cpp FnIteratorType.cpp + HeapBufferType.cpp ImagType.cpp IntType.cpp LoopExprIteratorType.cpp @@ -42,6 +43,7 @@ target_sources(ChplFrontend-obj NothingType.cpp Param.cpp PrimitiveType.cpp + PtrType.cpp PromotionIteratorType.cpp QualifiedType.cpp RealType.cpp diff --git a/frontend/lib/types/CPtrType.cpp b/frontend/lib/types/CPtrType.cpp index f510086be5d..7a9042331ac 100644 --- a/frontend/lib/types/CPtrType.cpp +++ b/frontend/lib/types/CPtrType.cpp @@ -38,14 +38,6 @@ const owned& CPtrType::getCPtrType(Context* context, return QUERY_END(result); } -bool CPtrType::isEltTypeInstantiationOf(Context* context, const CPtrType* other) const { - auto r = resolution::canPass(context, - QualifiedType(QualifiedType::TYPE, eltType_), - QualifiedType(QualifiedType::TYPE, other->eltType_)); - // instantiation and same-type passing are allowed here - return r.passes() && !r.promotes() && !r.converts(); -} - const CPtrType* CPtrType::get(Context* context) { return CPtrType::getCPtrType(context, /* instantiatedFrom */ nullptr, @@ -90,7 +82,7 @@ const ID& CPtrType::getId(Context* context) { const CPtrType* CPtrType::withoutConst(Context* context) const { const CPtrType* instFrom = nullptr; if (instantiatedFrom_) { - instFrom = instantiatedFrom_->withoutConst(context); + instFrom = instantiatedFrom_->toCPtrType()->withoutConst(context); } return CPtrType::getCPtrType(context, instFrom, eltType_, /* isConst */ false).get(); @@ -103,23 +95,6 @@ const ID& CPtrType::getConstId(Context* context) { return QUERY_END(result); } -bool CPtrType::isInstantiationOf(Context* context, const CPtrType* genericType) const { - auto thisFrom = instantiatedFromCPtrType(); - auto argFrom = genericType->instantiatedFromCPtrType(); - if (argFrom == nullptr) { - // if genericType is not a partial instantiation - return (thisFrom != nullptr && thisFrom == genericType); - } - - if (thisFrom == argFrom) { - // handle the case of genericType being partly instantiated - // (or instantiated with a generic type) - return isEltTypeInstantiationOf(context, genericType); - } - - return false; -} - void CPtrType::stringify(std::ostream& ss, chpl::StringifyKind stringKind) const { if (isConst_) { diff --git a/frontend/lib/types/HeapBufferType.cpp b/frontend/lib/types/HeapBufferType.cpp new file mode 100644 index 00000000000..b66b91fc554 --- /dev/null +++ b/frontend/lib/types/HeapBufferType.cpp @@ -0,0 +1,74 @@ +/* + * Copyright 2021-2024 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 "chpl/types/HeapBufferType.h" + +#include "chpl/framework/query-impl.h" +#include "chpl/parsing/parsing-queries.h" +#include "chpl/resolution/intents.h" +#include "chpl/types/Param.h" +#include "chpl/types/VoidType.h" +#include "chpl/resolution/can-pass.h" + +namespace chpl { +namespace types { + +const owned& HeapBufferType::getHeapBufferType(Context* context, + const HeapBufferType* instantiatedFrom, + const Type* eltType) { + QUERY_BEGIN(getHeapBufferType, context, instantiatedFrom, eltType); + auto result = toOwned(new HeapBufferType(instantiatedFrom, eltType)); + return QUERY_END(result); +} + +const HeapBufferType* HeapBufferType::get(Context* context) { + return HeapBufferType::getHeapBufferType(context, + /* instantiatedFrom */ nullptr, + /* eltType */ nullptr) + .get(); +} + +const HeapBufferType* HeapBufferType::get(Context* context, + const Type* eltType) { + return HeapBufferType::getHeapBufferType( + context, + /* instantiatedFrom */ HeapBufferType::get(context), eltType) + .get(); +} + +const ID& HeapBufferType::getId(Context* context) { + QUERY_BEGIN(getId, context); + ID result = + parsing::getSymbolIdFromTopLevelModule(context, "ChapelBase", "_ddata"); + return QUERY_END(result); +} + +void HeapBufferType::stringify(std::ostream& ss, + chpl::StringifyKind stringKind) const { + ss << "_ddata"; + + if (eltType_) { + ss << "("; + eltType_->stringify(ss, stringKind); + ss << ")"; + } +} + +} // end namespace types +} // end namespace chpl diff --git a/frontend/lib/types/PtrType.cpp b/frontend/lib/types/PtrType.cpp new file mode 100644 index 00000000000..64aee613446 --- /dev/null +++ b/frontend/lib/types/PtrType.cpp @@ -0,0 +1,55 @@ +/* + * Copyright 2021-2024 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 "chpl/types/PtrType.h" + +#include "chpl/resolution/can-pass.h" + +namespace chpl { +namespace types { + +bool PtrType::isEltTypeInstantiationOf(Context* context, + const PtrType* other) const { + auto r = + resolution::canPass(context, QualifiedType(QualifiedType::TYPE, eltType_), + QualifiedType(QualifiedType::TYPE, other->eltType_)); + // instantiation and same-type passing are allowed here + return r.passes() && !r.promotes() && !r.converts(); +} + +bool PtrType::isInstantiationOf(Context* context, const PtrType* genericType) const { + auto thisFrom = instantiatedFromPtrType(); + auto argFrom = genericType->instantiatedFromPtrType(); + if (argFrom == nullptr) { + // if genericType is not a partial instantiation + return (thisFrom != nullptr && thisFrom == genericType); + } + + if (thisFrom == argFrom) { + // handle the case of genericType being partly instantiated + // (or instantiated with a generic type) + return isEltTypeInstantiationOf(context, genericType); + } + + return false; +} + + +} // end namespace types +} // end namespace chpl diff --git a/frontend/lib/types/Type.cpp b/frontend/lib/types/Type.cpp index eb09da790b2..ee9f0792741 100644 --- a/frontend/lib/types/Type.cpp +++ b/frontend/lib/types/Type.cpp @@ -29,11 +29,13 @@ #include "chpl/types/ComplexType.h" #include "chpl/types/CPtrType.h" #include "chpl/types/DomainType.h" +#include "chpl/types/HeapBufferType.h" #include "chpl/types/ImagType.h" #include "chpl/types/IntType.h" #include "chpl/types/NilType.h" #include "chpl/types/NothingType.h" #include "chpl/types/PrimitiveType.h" +#include "chpl/types/PtrType.h" #include "chpl/types/RealType.h" #include "chpl/types/RecordType.h" #include "chpl/types/UintType.h" @@ -105,6 +107,7 @@ void Type::gatherBuiltins(Context* context, gatherType(context, map, "chpl_c_string", CStringType::get(context)); gatherType(context, map, "nothing", NothingType::get(context)); gatherType(context, map, "void", VoidType::get(context)); + gatherType(context, map, "_ddata", HeapBufferType::get(context)); gatherType(context, map, "RootClass", BasicClassType::getRootClassType(context)); @@ -188,7 +191,7 @@ bool Type::isLocaleType() const { } bool Type::isNilablePtrType() const { - if (isPtrType()) { + if (isAnyPtrType()) { if (auto ct = toClassType()) { if (!ct->decorator().isNilable()) diff --git a/frontend/test/resolution/CMakeLists.txt b/frontend/test/resolution/CMakeLists.txt index f8c555b44a2..27bc25c376b 100644 --- a/frontend/test/resolution/CMakeLists.txt +++ b/frontend/test/resolution/CMakeLists.txt @@ -43,6 +43,7 @@ comp_unit_test(testForwarding) comp_unit_test(testFunctionCalls) comp_unit_test(testGenericDefaults) comp_unit_test(testGetSymbolsAvailableInScope) +comp_unit_test(testHeapBuffer) comp_unit_test(testIf) comp_unit_test(testInitSemantics) comp_unit_test(testInteractive) diff --git a/frontend/test/resolution/testHeapBuffer.cpp b/frontend/test/resolution/testHeapBuffer.cpp new file mode 100644 index 00000000000..53396dda1df --- /dev/null +++ b/frontend/test/resolution/testHeapBuffer.cpp @@ -0,0 +1,230 @@ +/* + * Copyright 2021-2024 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-resolution.h" +#include "test-minimal-modules.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" + +#include + +// Use a single context with revisions to get this test running faster. +static Context* context; + +template +void testHeapBufferArg(const char* formalType, const char* actualType, F&& test) { + context->advanceToNextRevision(false); + if (!context->chplHome().empty()) + setupModuleSearchPaths(context, false, false, {}, {}); + ErrorGuard guard(context); + + std::stringstream ss; + + ss << "record rec { type someType; }" << std::endl; + ss << "proc f(x: " << formalType << ") {}" << std::endl; + ss << "var arg: " << actualType << ";" << std::endl; + ss << "var x = f(arg);" << std::endl; + + auto program = ss.str(); + + auto filePath = UniqueString::get(context, "testFile.chpl"); + setFileText(context, filePath, std::move(program)); + auto modules = parseToplevel(context, filePath); + + assert(modules.size() == 1); + auto mainMod = modules[0]; + assert(mainMod->numStmts() == 4); + + auto fChild = mainMod->child(1); + assert(fChild->isFunction()); + auto fFn = fChild->toFunction(); + assert(fFn->name() == "f"); + + auto fCallVar = mainMod->child(3); + assert(fCallVar->isVariable()); + + auto& modResResult = resolveModule(context, mainMod->id()); + auto& rr = modResResult.byAst(fCallVar->toVariable()->initExpression()); + + auto fn = rr.mostSpecific().only().fn(); + + const types::HeapBufferType* formalTypePtr = nullptr; + if (fn != nullptr) { + auto formalQt = fn->formalType(0); + if (auto tt = formalQt.type()) { + formalTypePtr = tt->toHeapBufferType(); + } + } + + test(fn, formalTypePtr, guard); +} + +static void test1() { + testHeapBufferArg("_ddata", "_ddata(int)", [](const TypedFnSignature* fn, const HeapBufferType* t, ErrorGuard& eg) { + assert(fn); + assert(t); + auto eltT = t->eltType(); + assert(eltT && eltT->isIntType()); + assert(eltT->toIntType()->isDefaultWidth()); + }); +} + +static void test2() { + testHeapBufferArg("_ddata", "_ddata(real)", [](const TypedFnSignature* fn, const HeapBufferType* t, ErrorGuard& eg) { + assert(fn); + assert(t); + auto eltT = t->eltType(); + assert(eltT && eltT->isRealType()); + assert(eltT->toRealType()->isDefaultWidth()); + }); +} + +static void test3() { + testHeapBufferArg("_ddata(int(?w))", "_ddata(int(32))", [](const TypedFnSignature* fn, const HeapBufferType* t, ErrorGuard& eg) { + assert(fn); + assert(t); + auto eltT = t->eltType(); + assert(eltT && eltT->isIntType()); + assert(eltT == IntType::get(eg.context(), 32)); + }); +} + +static void test4() { + testHeapBufferArg("_ddata(rec(?t))", "_ddata(rec(int))", [](const TypedFnSignature* fn, const HeapBufferType* t, ErrorGuard& eg) { + assert(fn); + assert(t); + auto eltT = t->eltType(); + assert(eltT && eltT->isRecordType()); + auto rt = eltT->toRecordType(); + assert(rt->name() == "rec"); + auto& fields = fieldsForTypeDecl(eg.context(), rt, DefaultsPolicy::IGNORE_DEFAULTS); + assert(fields.numFields() == 1 && fields.fieldType(0).type()->isIntType()); + }); +} + +static void test5() { + testHeapBufferArg("_ddata(int(?w))", "_ddata(uint(32))", [](const TypedFnSignature* fn, const HeapBufferType* t, ErrorGuard& eg) { + assert(!fn); + assert(eg.realizeErrors() == 1); + }); +} + +static void test6() { + testHeapBufferArg("_ddata(int(64))", "_ddata(int(32))", [](const TypedFnSignature* fn, const HeapBufferType* t, ErrorGuard& eg) { + assert(!fn); + assert(eg.realizeErrors() == 1); + }); +} + +static void test7() { + testHeapBufferArg("_ddata(int)", "_ddata(int)", [](const TypedFnSignature* fn, const HeapBufferType* t, ErrorGuard& eg) { + assert(fn); + assert(t); + auto eltT = t->eltType(); + assert(eltT && eltT->isIntType()); + assert(eltT->toIntType()->isDefaultWidth()); + }); +} + +static void test8() { + context->advanceToNextRevision(false); + if (!context->chplHome().empty()) + setupModuleSearchPaths(context, false, false, {}, {}); + ErrorGuard guard(context); + + std::string program = R"""( + module M{ + module X { + proc foo() { + var ret : _ddata(int); + return ret; + } + } + + use X; + + var ptr = foo(); + var x = ptr[0]; + } + )"""; + + auto vars = resolveTypesOfVariables(context, program, {"ptr", "x"}); + assert(vars["ptr"].type()->isHeapBufferType()); + assert(vars["x"].type()->isIntType()); + + assert(guard.realizeErrors() == 0); +} + +static void test9() { + context->advanceToNextRevision(false); + if (!context->chplHome().empty()) + setupModuleSearchPaths(context, false, false, {}, {}); + ErrorGuard guard(context); + + std::string program = R"""( + module M { + var ptr : _ddata(int); + type x = ptr.eltType; + } + )"""; + + auto vars = resolveTypesOfVariables(context, program, {"ptr", "x"}); + assert(vars["ptr"].type()->isHeapBufferType()); + assert(vars["x"].type()->isIntType()); + + assert(guard.realizeErrors() == 0); +} + +static void runAllTests() { + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); + test7(); + test8(); + test9(); +} + +int main() { + // With stdlib + { + context = new Context(getConfigWithHome()); + runAllTests(); + delete context; + } + + // Without stdlib + { + context = new Context(); + runAllTests(); + delete context; + } + + return 0; +}