From 11b29eff99910d55a54658b8a1d053cc4ec076fc Mon Sep 17 00:00:00 2001 From: Yong He Date: Thu, 4 Aug 2022 14:05:02 -0700 Subject: Implicit pointer dereference when using member operator. (#2348) * Implicit pointer dereference when using member operator. * Add expected test result * Fix lookup. Co-authored-by: Yong He --- source/slang/core.meta.slang | 28 ++++++++-------------- source/slang/slang-ast-support-types.h | 1 + source/slang/slang-check-expr.cpp | 12 ++++++---- source/slang/slang-check-impl.h | 4 ++++ source/slang/slang-check-overload.cpp | 4 +++- source/slang/slang-check-type.cpp | 13 ++++++++++ source/slang/slang-emit-c-like.cpp | 31 +++++++++++++++++++----- source/slang/slang-lookup.cpp | 27 +++++++++++---------- tests/cpu-program/pointer-deref.slang | 33 ++++++++++++++++++++++++++ tests/cpu-program/pointer-deref.slang.expected | 6 +++++ 10 files changed, 117 insertions(+), 42 deletions(-) create mode 100644 tests/cpu-program/pointer-deref.slang create mode 100644 tests/cpu-program/pointer-deref.slang.expected diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang index 1991eb583..862595b90 100644 --- a/source/slang/core.meta.slang +++ b/source/slang/core.meta.slang @@ -358,23 +358,6 @@ __magic_type(PtrType) __intrinsic_type($(kIROp_PtrType)) struct Ptr { - __intrinsic_op($(kIROp_Load)) - static T __load(Ptr ptr); - - __intrinsic_op($(kIROp_Store)) - static void __store(Ptr ptr, T val); - - __intrinsic_op($(kIROp_getElementPtr)) - static Ptr __getElementPtr(Ptr ptr, int index); - - __intrinsic_op($(kIROp_Neq)) - __generic - static bool __neq(Ptr ptr, U val); - - __intrinsic_op($(kIROp_Eql)) - __generic - static bool __eql(Ptr ptr, U val); - __generic __intrinsic_op($(kIROp_BitCast)) __init(Ptr ptr); @@ -404,6 +387,15 @@ struct Ptr } }; +__intrinsic_op($(kIROp_Load)) +T __load(Ptr ptr); + +__intrinsic_op($(kIROp_Store)) +void __store(Ptr ptr, T val); + +__intrinsic_op($(kIROp_getElementPtr)) +Ptr __getElementPtr(Ptr ptr, int index); + __generic __intrinsic_op($(kIROp_Less)) bool operator<(Ptr p1, Ptr p2); @@ -1726,7 +1718,7 @@ __generic [__unsafeForceInlineEarly] Ptr operator-(Ptr value, int64_t offset) { - return Ptr.__getElementPtr(value, -offset); + return __getElementPtr(value, -offset); } __generic diff --git a/source/slang/slang-ast-support-types.h b/source/slang/slang-ast-support-types.h index 3c161877c..f74abc08f 100644 --- a/source/slang/slang-ast-support-types.h +++ b/source/slang/slang-ast-support-types.h @@ -1121,6 +1121,7 @@ namespace Slang None = 0, IgnoreBaseInterfaces = 1 << 0, Completion = 1 << 1, ///< Lookup all applicable decls for code completion suggestions + NoDeref = 1 << 2, }; class SerialRefObject; diff --git a/source/slang/slang-check-expr.cpp b/source/slang/slang-check-expr.cpp index addd3a5c4..b7f99c4e7 100644 --- a/source/slang/slang-check-expr.cpp +++ b/source/slang/slang-check-expr.cpp @@ -382,15 +382,15 @@ namespace Slang Expr* base, SourceLoc loc) { - auto ptrLikeType = as(base->type); - SLANG_ASSERT(ptrLikeType); + auto elementType = getPointedToTypeIfCanImplicitDeref(base->type); + SLANG_ASSERT(elementType); auto derefExpr = m_astBuilder->create(); derefExpr->loc = loc; derefExpr->base = base; - derefExpr->type = QualType(ptrLikeType->elementType); + derefExpr->type = QualType(elementType); - // TODO(tfoley): handle l-value status here + derefExpr->type.isLeftValue = base->type.isLeftValue; return derefExpr; } @@ -1258,7 +1258,9 @@ namespace Slang m_astBuilder, this, name, - baseType); + baseType, + LookupMask::Default, + LookupOptions::NoDeref); if (!lookupResult.isValid()) { goto fail; diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h index c7b3f432a..ebc6e05d5 100644 --- a/source/slang/slang-check-impl.h +++ b/source/slang/slang-check-impl.h @@ -21,6 +21,10 @@ namespace Slang TypeExp typeExp, DiagnosticSink* sink); + /// Get the element type if `type` is Ptr or PtrLike type, otherwise returns null. + /// Note: this currently does not include PtrTypeBase. + Type* getPointedToTypeIfCanImplicitDeref(Type* type); + // A flat representation of basic types (scalars, vectors and matrices) // that can be used as lookup key in caches enum class BasicTypeKey : uint16_t diff --git a/source/slang/slang-check-overload.cpp b/source/slang/slang-check-overload.cpp index abb049a84..879c49da0 100644 --- a/source/slang/slang-check-overload.cpp +++ b/source/slang/slang-check-overload.cpp @@ -1242,7 +1242,9 @@ namespace Slang m_astBuilder, this, getName("$init"), - type); + type, + LookupMask::Default, + LookupOptions::NoDeref); AddOverloadCandidates(initializers, context); } diff --git a/source/slang/slang-check-type.cpp b/source/slang/slang-check-type.cpp index dc0d69f04..50bc55641 100644 --- a/source/slang/slang-check-type.cpp +++ b/source/slang/slang-check-type.cpp @@ -22,6 +22,19 @@ namespace Slang return typeOut.type; } + Type* getPointedToTypeIfCanImplicitDeref(Type* type) + { + if (auto ptrLike = as(type)) + { + return ptrLike->getElementType(); + } + else if (auto ptrType = as(type)) + { + return ptrType->getValueType(); + } + return nullptr; + } + Expr* SemanticsVisitor::TranslateTypeNodeImpl(Expr* node) { if (!node) return nullptr; diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp index b6540b5f1..226e29764 100644 --- a/source/slang/slang-emit-c-like.cpp +++ b/source/slang/slang-emit-c-like.cpp @@ -1291,20 +1291,39 @@ bool CLikeSourceEmitter::shouldFoldInstIntoUseSites(IRInst* inst) void CLikeSourceEmitter::emitDereferenceOperand(IRInst* inst, EmitOpInfo const& outerPrec) { + EmitOpInfo newOuterPrec = outerPrec; + if (doesTargetSupportPtrTypes()) { - // If `inst` is a variable, dereferencing it is equivalent to just - // emit its name. i.e. *&var ==> var. - // We apply this peep hole optimization here to reduce the clutter of - // resulting code. - if (inst->getOp() == kIROp_Var) + switch (inst->getOp()) { + case kIROp_Var: + // If `inst` is a variable, dereferencing it is equivalent to just + // emit its name. i.e. *&var ==> var. + // We apply this peep hole optimization here to reduce the clutter of + // resulting code. m_writer->emit(getName(inst)); return; + case kIROp_FieldAddress: + { + auto innerPrec = getInfo(EmitOp::Postfix); + bool innerNeedClose = maybeEmitParens(newOuterPrec, innerPrec); + auto ii = as(inst); + auto base = ii->getBase(); + if (isPtrToClassType(base->getDataType())) + emitDereferenceOperand(base, leftSide(newOuterPrec, innerPrec)); + else + emitOperand(base, leftSide(newOuterPrec, innerPrec)); + m_writer->emit("->"); + m_writer->emit(getName(ii->getField())); + maybeCloseParens(innerNeedClose); + return; + } + default: + break; } auto dereferencePrec = EmitOpInfo::get(EmitOp::Prefix); - EmitOpInfo newOuterPrec = outerPrec; bool needClose = maybeEmitParens(newOuterPrec, dereferencePrec); m_writer->emit("*"); emitOperand(inst, rightSide(newOuterPrec, dereferencePrec)); diff --git a/source/slang/slang-lookup.cpp b/source/slang/slang-lookup.cpp index 8c69e2013..85924ab30 100644 --- a/source/slang/slang-lookup.cpp +++ b/source/slang/slang-lookup.cpp @@ -607,19 +607,22 @@ static void _lookUpMembersInSuperTypeImpl( { // If the type was pointer-like, then dereference it // automatically here. - if (auto pointerLikeType = as(superType)) + if (((uint32_t)request.options & (uint32_t)LookupOptions::NoDeref) == 0) { - // Need to leave a breadcrumb to indicate that we - // did an implicit dereference here - BreadcrumbInfo derefBreacrumb; - derefBreacrumb.kind = LookupResultItem::Breadcrumb::Kind::Deref; - derefBreacrumb.prev = inBreadcrumbs; - - // Recursively perform lookup on the result of deref - _lookUpMembersInType( - astBuilder, - name, pointerLikeType->elementType, request, ioResult, &derefBreacrumb); - return; + if (auto pointerElementType = getPointedToTypeIfCanImplicitDeref(superType)) + { + // Need to leave a breadcrumb to indicate that we + // did an implicit dereference here + BreadcrumbInfo derefBreacrumb; + derefBreacrumb.kind = LookupResultItem::Breadcrumb::Kind::Deref; + derefBreacrumb.prev = inBreadcrumbs; + + // Recursively perform lookup on the result of deref + _lookUpMembersInType( + astBuilder, + name, pointerElementType, request, ioResult, &derefBreacrumb); + return; + } } // Default case: no dereference needed diff --git a/tests/cpu-program/pointer-deref.slang b/tests/cpu-program/pointer-deref.slang new file mode 100644 index 000000000..34c6ba97f --- /dev/null +++ b/tests/cpu-program/pointer-deref.slang @@ -0,0 +1,33 @@ +//TEST:EXECUTABLE: +__target_intrinsic(cpp, "printf(\"%s\\n\", ($0).getBuffer())") +void writeln(String text); + +struct SubRecord +{ + int field2; + float field3; +} + +struct Record +{ + int field; + SubRecord sub; +} + +public __extern_cpp int main() +{ + Record rec; + Record *pRec = &rec; + pRec.field = 1; + pRec.sub.field2 = 2; + pRec.sub.field3 = 3.0f; + if (rec.field == 1 && rec.sub.field2 == 2 && pRec.sub.field3 == 3.0f) + { + writeln("success"); + } + else + { + writeln("fail"); + } + return 0; +} \ No newline at end of file diff --git a/tests/cpu-program/pointer-deref.slang.expected b/tests/cpu-program/pointer-deref.slang.expected new file mode 100644 index 000000000..0eb55acbf --- /dev/null +++ b/tests/cpu-program/pointer-deref.slang.expected @@ -0,0 +1,6 @@ +result code = 0 +standard error = { +} +standard output = { +success +} -- cgit v1.2.3