summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2018-10-18 09:30:38 -0700
committerGitHub <noreply@github.com>2018-10-18 09:30:38 -0700
commit3a5214b65b2a5efdbcf9bf6fb4d7603e9ee63234 (patch)
treeaa77f0e7b5ca7e6327c252a8acff50ccb08a7794
parentf9710d50bc675ddba51cc6d94b125ba1549708a8 (diff)
Add support for static methods in interfaces (#680)
This change allows an interface to include `static` methods as requirements, so that types that conform to the interface will need to satisfy the requirement with a `static` method. The essence of the check is simple: when checking that a method satisfies a requirement, we enforce that both are `static` or both are non-`static`. Making that simple change and adding a test change broke a few other places in the compiler that this change tries to fix. The main fix is to handle cases where we might look up an "effectively static" member of a type through an instance, and to make sure that we replace the instance-based lookup with type-based lookup. There was already logic along these lines in `lower-to-ir.cpp`, so this change centralizes it in `check.cpp` where it seems to logically belong.
-rw-r--r--source/slang/check.cpp92
-rw-r--r--source/slang/lower-to-ir.cpp41
-rw-r--r--tests/compute/interface-static-method.slang56
-rw-r--r--tests/compute/interface-static-method.slang.expected.txt4
4 files changed, 159 insertions, 34 deletions
diff --git a/source/slang/check.cpp b/source/slang/check.cpp
index 985c5c0b7..47713e6cc 100644
--- a/source/slang/check.cpp
+++ b/source/slang/check.cpp
@@ -9,6 +9,75 @@
namespace Slang
{
+
+ /// Should the given `decl` nested in `parentDecl` be treated as a static rather than instance declaration?
+ bool isEffectivelyStatic(
+ Decl* decl,
+ ContainerDecl* parentDecl)
+ {
+ // Things at the global scope are always "members" of their module.
+ //
+ if(parentDecl->As<ModuleDecl>())
+ return false;
+
+ // Anything explicitly marked `static` and not at module scope
+ // counts as a static rather than instance declaration.
+ //
+ if(decl->HasModifier<HLSLStaticModifier>())
+ return true;
+
+ // Next we need to deal with cases where a declaration is
+ // effectively `static` even if the language doesn't make
+ // the user say so. Most languages make the default assumption
+ // that nested types are `static` even if they don't say
+ // so (Java is an exception here, perhaps due to some
+ // influence from the Scandanavian OOP tradition of Beta/gbeta).
+ //
+ if(dynamic_cast<AggTypeDecl*>(decl))
+ return true;
+ if(dynamic_cast<SimpleTypeDecl*>(decl))
+ return true;
+
+ // Things nested inside functions may have dependencies
+ // on values from the enclosing scope, but this needs to
+ // be dealt with via "capture" so they are also effectively
+ // `static`
+ //
+ if(dynamic_cast<FunctionDeclBase*>(parentDecl))
+ return true;
+
+ // Type constraint declarations are used in member-reference
+ // context as a form of casting operation, so we treat them
+ // as if they are instance members. This is a bit of a hack,
+ // but it achieves the result we want until we have an
+ // explicit representation of up-cast operations in the
+ // AST.
+ //
+ if(decl->As<TypeConstraintDecl>())
+ return false;
+
+ return false;
+ }
+
+ /// Should the given `decl` be treated as a static rather than instance declaration?
+ bool isEffectivelyStatic(
+ Decl* decl)
+ {
+ // For the purposes of an ordinary declaration, when determining if
+ // it is static or per-instance, the "parent" declaration we really
+ // care about is the next outer non-generic declaration.
+ //
+ // TODO: This idiom of getting the "next outer non-generic declaration"
+ // comes up just enough that we should probably have a convenience
+ // function for it.
+
+ auto parentDecl = decl->ParentDecl;
+ if(auto genericDecl = parentDecl->As<GenericDecl>())
+ parentDecl = genericDecl->ParentDecl;
+
+ return isEffectivelyStatic(decl, parentDecl);
+ }
+
// A flat representation of basic types (scalars, vectors and matrices)
// that can be used as lookup key in caches
struct BasicTypeKey
@@ -416,6 +485,22 @@ namespace Slang
expr->declRef = declRef;
return expr;
}
+ else if(isEffectivelyStatic(declRef.getDecl()))
+ {
+ // Extract the type of the baseExpr
+ auto baseExprType = baseExpr->type.type;
+ RefPtr<SharedTypeExpr> baseTypeExpr = new SharedTypeExpr();
+ baseTypeExpr->base.type = baseExprType;
+ baseTypeExpr->type = new TypeType(baseExprType);
+
+ auto expr = new StaticMemberExpr();
+ expr->loc = loc;
+ expr->type = type;
+ expr->BaseExpression = baseTypeExpr;
+ expr->name = declRef.GetName();
+ expr->declRef = declRef;
+ return expr;
+ }
else
{
// If the base expression wasn't a type, then this
@@ -2246,6 +2331,13 @@ namespace Slang
return false;
}
+ if(satisfyingMemberDeclRef.getDecl()->HasModifier<HLSLStaticModifier>()
+ != requiredMemberDeclRef.getDecl()->HasModifier<HLSLStaticModifier>())
+ {
+ // A `static` method can't satisfy a non-`static` requirement and vice versa.
+ return false;
+ }
+
// TODO: actually implement matching here. For now we'll
// just pretend that things are satisfied in order to make progress..
witnessTable->requirementDictionary.Add(
diff --git a/source/slang/lower-to-ir.cpp b/source/slang/lower-to-ir.cpp
index 3c4067af5..7b63f510f 100644
--- a/source/slang/lower-to-ir.cpp
+++ b/source/slang/lower-to-ir.cpp
@@ -384,6 +384,12 @@ void setValue(IRGenContext* context, Decl* decl, LoweredValInfo value)
context->env->mapDeclToValue[decl] = value;
}
+
+ /// Should the given `decl` nested in `parentDecl` be treated as a static rather than instance declaration?
+bool isEffectivelyStatic(
+ Decl* decl,
+ ContainerDecl* parentDecl);
+
// Ensure that a version of the given declaration has been emitted to the IR
LoweredValInfo ensureDecl(
IRGenContext* context,
@@ -4446,46 +4452,13 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
//
// We also need to be able to detect whether a declaration is
// either explicitly or implicitly treated as `static`:
- bool isMemberDeclarationEffectivelyStatic(
- Decl* decl,
- ContainerDecl* parentDecl)
- {
- // Anything explicitly marked `static` counts.
- //
- // There is a subtle detail here with a global-scope `static`
- // variable not really meaning `static` in the same way, but
- // it doesn't matter because the module shouldn't introduce
- // any parameters we care about.
- if(decl->HasModifier<HLSLStaticModifier>())
- return true;
-
- // Next we need to deal with cases where a declaration is
- // effectively `static` even if the language doesn't make
- // the user say so. Most languages make the default assumption
- // that nested types are `static` even if they don't say
- // so (Java is an exception here, perhaps due to some
- // includence from the Scandanavian OOP tradition).
- if(dynamic_cast<AggTypeDecl*>(decl))
- return true;
-
- // Things nested inside functions may have dependencies
- // on values from the enclosing scope, but this needs to
- // be dealt with via "capture" so they are also effectively
- // `static`
- if(dynamic_cast<FunctionDeclBase*>(parentDecl))
- return true;
-
- return false;
- }
- // We also need to be able to detect whether a declaration is
- // either explicitly or implicitly treated as `static`:
ParameterListCollectMode getModeForCollectingParentParameters(
Decl* decl,
ContainerDecl* parentDecl)
{
// If we have a `static` parameter, then it is obvious
// that we should use the `static` mode
- if(isMemberDeclarationEffectivelyStatic(decl, parentDecl))
+ if(isEffectivelyStatic(decl, parentDecl))
return kParameterListCollectMode_Static;
// Otherwise, let's default to collecting everything
diff --git a/tests/compute/interface-static-method.slang b/tests/compute/interface-static-method.slang
new file mode 100644
index 000000000..4747c8a8a
--- /dev/null
+++ b/tests/compute/interface-static-method.slang
@@ -0,0 +1,56 @@
+// interface-static-method.slang
+
+//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute
+//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -dx12
+//TEST(compute, vulkan):COMPARE_COMPUTE_EX:-vk -compute
+
+interface IHideout
+{
+ int getAnimalCount();
+}
+
+interface ISuperhero
+{
+ associatedtype Hideout : IHideout;
+
+ static Hideout getHideout();
+}
+
+struct Batcave : IHideout
+{
+ int batCount;
+
+ int getAnimalCount() { return batCount; }
+}
+
+struct Batman : ISuperhero
+{
+ typedef Batcave Hideout;
+
+ static Batcave getHideout()
+ {
+ Batcave batcave = { 100 };
+ return batcave;
+ }
+}
+
+int doIt<T:ISuperhero>()
+{
+ return T.getHideout().getAnimalCount();
+}
+
+int test(int val)
+{
+ return doIt<Batman>();
+}
+
+
+//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):dxbinding(0),glbinding(0),out
+RWStructuredBuffer<int> outputBuffer;
+
+[numthreads(4, 1, 1)]
+void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID)
+{
+ int tid = dispatchThreadID.x;
+ outputBuffer[tid] = test(tid);
+} \ No newline at end of file
diff --git a/tests/compute/interface-static-method.slang.expected.txt b/tests/compute/interface-static-method.slang.expected.txt
new file mode 100644
index 000000000..b1b8e46f4
--- /dev/null
+++ b/tests/compute/interface-static-method.slang.expected.txt
@@ -0,0 +1,4 @@
+64
+64
+64
+64