summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/user-guide/03-convenience-features.md8
-rw-r--r--source/slang/slang-ast-expr.h6
-rw-r--r--source/slang/slang-check-expr.cpp24
-rw-r--r--source/slang/slang-check-impl.h2
-rw-r--r--source/slang/slang-diagnostic-defs.h1
-rw-r--r--source/slang/slang-language-server.cpp29
-rw-r--r--source/slang/slang-parser.cpp13
-rw-r--r--tests/diagnostics/arrow-operator.slang25
-rw-r--r--tests/spirv/pointer.slang2
9 files changed, 87 insertions, 23 deletions
diff --git a/docs/user-guide/03-convenience-features.md b/docs/user-guide/03-convenience-features.md
index ca4635df4..a325f64f3 100644
--- a/docs/user-guide/03-convenience-features.md
+++ b/docs/user-guide/03-convenience-features.md
@@ -363,19 +363,21 @@ float4 myPackedVector = reinterpret<float4>(myVal);
## Pointers
-Slang supports pointers when generating code for SPIRV, C++ and CUDA targets. The syntax for pointers is similar to C, with the exception that operator `.` can also be used to dereference a member. Slang currently does not support the `->` operator.
-For example:
+Slang supports pointers when generating code for SPIRV, C++ and CUDA targets. The syntax for pointers is similar to C, with the exception that operator `.` can also be used to dereference a member from a pointer. For example:
```csharp
int test(MyType* pObj)
{
MyType* pNext = pObj + 1;
MyType* pNext = &pNext[1];
- return pNext.a + (*pNext).a + pNext[0].a;
+ return pNext.a + pNext->a + (*pNext).a + pNext[0].a;
}
```
Pointer types can also be specified using the generic syntax: `Ptr<MyType>` is equivalent to `MyType*`.
+> #### Note
+> Slang currently does not support pointers to immutable values, i.e. `const T*`.
+
## `struct` inheritance (limited)
Slang supports a limited form of inheritance. A derived `struct` type has all the members defined in the base type it is inherited from:
diff --git a/source/slang/slang-ast-expr.h b/source/slang/slang-ast-expr.h
index 3331b288e..56ac83f80 100644
--- a/source/slang/slang-ast-expr.h
+++ b/source/slang/slang-ast-expr.h
@@ -238,6 +238,12 @@ class MemberExpr: public DeclRefExpr
SourceLoc memberOperatorLoc;
};
+// Member expression that is dereferenced, e.g. `a->b`.
+class DerefMemberExpr : public MemberExpr
+{
+ SLANG_AST_CLASS(DerefMemberExpr)
+};
+
// Member looked up on a type, rather than a value
class StaticMemberExpr: public DeclRefExpr
{
diff --git a/source/slang/slang-check-expr.cpp b/source/slang/slang-check-expr.cpp
index f238550fd..dc7a2d35b 100644
--- a/source/slang/slang-check-expr.cpp
+++ b/source/slang/slang-check-expr.cpp
@@ -1918,7 +1918,8 @@ namespace Slang
Expr* SemanticsExprVisitor::visitIndexExpr(IndexExpr* subscriptExpr)
{
- auto baseExpr = checkBaseForMemberExpr(subscriptExpr->baseExpression);
+ bool needDeref = false;
+ auto baseExpr = checkBaseForMemberExpr(subscriptExpr->baseExpression, needDeref);
// If the base expression is a type, it means that this is an array declaration,
// then we should disable short-circuit in case there is logical expression in
@@ -3833,13 +3834,18 @@ namespace Slang
return expr;
}
- Expr* SemanticsVisitor::checkBaseForMemberExpr(Expr* inBaseExpr)
+ Expr* SemanticsVisitor::checkBaseForMemberExpr(Expr* inBaseExpr, bool& outNeedDeref)
{
auto baseExpr = inBaseExpr;
baseExpr = CheckTerm(baseExpr);
- baseExpr = MaybeDereference(baseExpr);
+ auto derefExpr = MaybeDereference(baseExpr);
+
+ if (derefExpr != baseExpr)
+ outNeedDeref = true;
+
+ baseExpr = derefExpr;
// If the base of the member lookup has an interface type
// *without* a suitable this-type substitution, then we are
@@ -3889,7 +3895,17 @@ namespace Slang
Expr* SemanticsExprVisitor::visitMemberExpr(MemberExpr * expr)
{
- expr->baseExpression = checkBaseForMemberExpr(expr->baseExpression);
+ bool needDeref = false;
+ expr->baseExpression = checkBaseForMemberExpr(expr->baseExpression, needDeref);
+
+ if (!needDeref && as<DerefMemberExpr>(expr) && !as<PtrType>(expr->baseExpression->type))
+ {
+ // The user is trying to use the `->` operator on something that can't be
+ // dereferenced, so we should diagnose that.
+ if (!as<ErrorType>(expr->baseExpression->type))
+ getSink()->diagnose(expr->memberOperatorLoc, Diagnostics::cannotDereferenceType, expr->baseExpression->type);
+ }
+
auto baseType = expr->baseExpression->type;
// If we are looking up through a modified type, just pass straight
diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h
index cff5b7028..b928a39f0 100644
--- a/source/slang/slang-check-impl.h
+++ b/source/slang/slang-check-impl.h
@@ -2505,7 +2505,7 @@ namespace Slang
Expr* visitStaticMemberExpr(StaticMemberExpr* expr);
/// Perform checking operations required for the "base" expression of a member-reference like `base.someField`
- Expr* checkBaseForMemberExpr(Expr* baseExpr);
+ Expr* checkBaseForMemberExpr(Expr* baseExpr, bool& outNeedDeref);
Expr* lookupMemberResultFailure(
DeclRefExpr* expr,
diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h
index 6299d6666..757ce94ee 100644
--- a/source/slang/slang-diagnostic-defs.h
+++ b/source/slang/slang-diagnostic-defs.h
@@ -327,6 +327,7 @@ DIAGNOSTIC(30068, Warning, mutatingMethodOnFunctionInputParameterWarning, "mutat
DIAGNOSTIC(30070, Error, unsizedMemberMustAppearLast, "unsized member can only appear as the last member in a composite type.")
DIAGNOSTIC(30100, Error, staticRefToNonStaticMember, "type '$0' cannot be used to refer to non-static member '$1'")
+DIAGNOSTIC(30101, Error, cannotDereferenceType, "cannot dereference type '$0', do you mean to use '.'?")
DIAGNOSTIC(30200, Error, redeclaration, "declaration of '$0' conflicts with existing declaration")
DIAGNOSTIC(30201, Error, functionRedefinition, "function '$0' already has a body")
diff --git a/source/slang/slang-language-server.cpp b/source/slang/slang-language-server.cpp
index b8ebf8e08..9cfef50a0 100644
--- a/source/slang/slang-language-server.cpp
+++ b/source/slang/slang-language-server.cpp
@@ -125,6 +125,7 @@ SlangResult LanguageServer::parseNextMessage()
caps.documentOnTypeFormattingProvider.moreTriggerCharacter.add("{");
caps.documentRangeFormattingProvider = true;
caps.completionProvider.triggerCharacters.add(".");
+ caps.completionProvider.triggerCharacters.add(">");
caps.completionProvider.triggerCharacters.add(":");
caps.completionProvider.triggerCharacters.add("[");
caps.completionProvider.triggerCharacters.add(" ");
@@ -931,17 +932,25 @@ SlangResult LanguageServer::completion(
return SLANG_OK;
}
- // Don't show completion at case label.
+ // Don't show completion at case label or after single '>' operator.
if (args.context.triggerKind ==
- LanguageServerProtocol::kCompletionTriggerKindTriggerCharacter &&
- args.context.triggerCharacter == ":")
- {
- auto line = doc->getLine((Int)args.position.line + 1);
- auto prevCharPos = args.position.character - 2;
- if (prevCharPos >= 0 && prevCharPos < line.getLength() && line[prevCharPos] != ':')
- {
- m_connection->sendResult(NullResponse::get(), responseId);
- return SLANG_OK;
+ LanguageServerProtocol::kCompletionTriggerKindTriggerCharacter)
+ {
+ char requiredPrevChar = 0;
+ if (args.context.triggerCharacter == ":")
+ requiredPrevChar = ':';
+ else if (args.context.triggerCharacter == ">")
+ requiredPrevChar = '-';
+ if (requiredPrevChar != 0)
+ {
+ // Check if the previous character is the required character (':' or '-'
+ auto line = doc->getLine((Int)args.position.line + 1);
+ auto prevCharPos = args.position.character - 2;
+ if (prevCharPos >= 0 && prevCharPos < line.getLength() && line[prevCharPos] != requiredPrevChar)
+ {
+ m_connection->sendResult(NullResponse::get(), responseId);
+ return SLANG_OK;
+ }
}
}
diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp
index ab69b66d8..d64329add 100644
--- a/source/slang/slang-parser.cpp
+++ b/source/slang/slang-parser.cpp
@@ -6884,7 +6884,8 @@ namespace Slang
auto expr = parseAtomicExpr(parser);
for(;;)
{
- switch( peekTokenType(parser) )
+ auto nextTokenType = peekTokenType(parser);
+ switch (nextTokenType)
{
default:
return expr;
@@ -6976,16 +6977,20 @@ namespace Slang
break;
}
- // Member access `x.m`
+ // Member access `x.m` or `x->m`
case TokenType::Dot:
+ case TokenType::RightArrow:
{
- MemberExpr* memberExpr = parser->astBuilder->create<MemberExpr>();
+
+ MemberExpr* memberExpr = nextTokenType == TokenType::Dot
+ ? parser->astBuilder->create<MemberExpr>()
+ : parser->astBuilder->create<DerefMemberExpr>();
// TODO(tfoley): why would a member expression need this?
memberExpr->scope = parser->currentScope;
memberExpr->memberOperatorLoc = parser->tokenReader.peekLoc();
memberExpr->baseExpression = expr;
- parser->ReadToken(TokenType::Dot);
+ parser->ReadToken(nextTokenType);
parser->FillPosition(memberExpr);
memberExpr->name = expectIdentifier(parser).name;
diff --git a/tests/diagnostics/arrow-operator.slang b/tests/diagnostics/arrow-operator.slang
new file mode 100644
index 000000000..8c370cf67
--- /dev/null
+++ b/tests/diagnostics/arrow-operator.slang
@@ -0,0 +1,25 @@
+//TEST:SIMPLE(filecheck=CHECK): -target spirv -emit-spirv-directly
+
+struct Type
+{
+ int member;
+}
+
+struct CB
+{
+ Type* ptr;
+}
+ConstantBuffer<CB> cb;
+
+[numthreads(1,1,1)]
+void main()
+{
+ Type val;
+
+ // CHECK: ([[# @LINE+1]]): error 30101
+ val->member = 2; // Error.
+
+ // CHECK-NOT: error
+ let a = cb->ptr->member; // OK.
+
+} \ No newline at end of file
diff --git a/tests/spirv/pointer.slang b/tests/spirv/pointer.slang
index cd4845e4d..03ca3fb39 100644
--- a/tests/spirv/pointer.slang
+++ b/tests/spirv/pointer.slang
@@ -33,7 +33,7 @@ RWStructuredBuffer<int> output;
void main(int id : SV_DispatchThreadID)
{
output[0] = buffer[0].pNext.data;
- let pData = &(buffer[0].pNext.data);
+ let pData = &(buffer[0].pNext->data); // operator -> is also allowed on pointer types.
// CHECK: OpPtrAccessChain
int* pData1 = pData + 1;
*pData1 = 3;