summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2020-03-06 11:37:36 -0800
committerGitHub <noreply@github.com>2020-03-06 11:37:36 -0800
commit18be2d81fd2740d3f0c06fc407cff1702b93d468 (patch)
tree2438f0cca4cd3453534d9742c2235cf3771c9194 /source
parent0b91ea73fe7eb1cf908fc1f87a9539a6fe48b65a (diff)
Expand range of definitions that can be moved into stdlib (#1259)
The actual definitions that got moved into the stdlib here are pretty few: * `clip()` * `cross()` * `dxx()`, `ddy()` etc. * `degrees()` * `distance()` * `dot()` * `faceforward()` The meat of the change is infrastructure changes required to support these new declarations * Generic versions of the standard operators (e.g., `operator+`) were added that are generic for a type `T` that implements the matching `__Builtin`-prefixed interface. An open question is whether we can now drop the non-generic versions in favor of just having these generic operators. * A `__BuiltinLogicalType` interface was added to capture the commonality between integers and `bool` * `__BuiltinArithmeticType` was extended so that implementations must support initialization from an `int` * `__BuiltinFloatingPointType` was extended to require an accessor that returns the value of pi for the given type, and the concrete floating-point types were extended to provide definitions of this value. * It turns out that our logic for checking if two functions have the same signature (and should thus count as redeclarations/redefinitions) wasn't taking generic constraints into account at all. That was fixed with a stopgap solution that checks if the generic constraints are pairwise identical, but I didn't implement the more "correct" fix that would require canonicalizing the constraints. * When doing overload resolution and considering potential callees, logic was added so that a non-generic candidate should always be selected over a generic one (generally the Right Thing to do), and also so that a generic candidate with fewer parameters will be selected over one with more (an approximation of the much more complicated rule we'd ideally have). * The formatting of declarations/overloads for "ambiguous overload" errors was fleshed out a bit to include more context (the "kind" of declaration where appropriate, the return type for function declarations) and to properly space thing when outputting specialization of operator overloads that end with `<` (so that we print `func < <int>(int, int)` instead of just `func <<int,int>(int,int)`). * The core lookup routines were heavily refactored and reorganized to try to make them bottleneck more effectively so that all paths handle all the nuances of inheritance, extensions, etc. * Because of the refactoring to lookup logic, the semantic checking logic related to checking if a type conforms to an interface was updated to be driven based on the `Type` that is supposed to be conforming, rather than a `DeclRef` to the type's declaration. This allows it to use the type-based lookup entry point and eliminates one special-case entry point for lookup. In addition to the various core changes, this change also refactors some of the existing stdlib code to favor writing more things in actual Slang syntax, and less in C++ code that uses `StringBuilder` to construct the Slang syntax. There is a lot more that could be done along those lines, but even pushing this far is showing that the current approach that `slang-generate` takes for how to separate meta-level C++ and Slang code isn't really ideal, so a revamp of the generator code is probably needed before I continue pushing. One surprising casualty of the refactoring of lookup is that we no longer have the `lookedUpDecls` field in `LookupResult`. That field probably didn't belong there anyway, but the role it served was important. The idea of `lookedUpDecls` was to avoid looking up in the same interface more than once in cases where a type might have a "diamond" inheritance pattern. Removing that field doesn't appear to affect correctness of any of our existing tests, but by adding a specific test for "diamond" inheritance I could see that the refactoring introduced a regression and made looking up a member inherited along multiple paths ambiguous. Rather than add back `lookedUpDecls` I went for a simpler (but arguably even hackier) solution where when ranking candidates from a `LookupResult` we check for identical `DeclRef`s and arbitrarily favor one over the other. One complication that arises here is that when comparing `DeclRef`s inherited along different paths they might have a `ThisTypeSubstitution` for the same type, but with different subtype witnesses (because different inheritance paths could lead to different transitive subtype witnesses: e.g., `A : B : D` and `A : C : D`).
Diffstat (limited to 'source')
-rw-r--r--source/slang/core.meta.slang292
-rw-r--r--source/slang/core.meta.slang.h375
-rw-r--r--source/slang/hlsl.meta.slang193
-rw-r--r--source/slang/hlsl.meta.slang.h195
-rw-r--r--source/slang/slang-check-decl.cpp434
-rw-r--r--source/slang/slang-check-impl.h37
-rw-r--r--source/slang/slang-check-overload.cpp223
-rw-r--r--source/slang/slang-emit-cpp.cpp31
-rw-r--r--source/slang/slang-emit-cpp.h1
-rw-r--r--source/slang/slang-hlsl-intrinsic-set.h1
-rw-r--r--source/slang/slang-lookup.cpp787
-rw-r--r--source/slang/slang-lookup.h9
-rw-r--r--source/slang/slang-stdlib.cpp78
-rw-r--r--source/slang/slang-syntax.cpp13
-rw-r--r--source/slang/slang-syntax.h2
15 files changed, 1763 insertions, 908 deletions
diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang
index 392922d38..2df6cb36b 100644
--- a/source/slang/core.meta.slang
+++ b/source/slang/core.meta.slang
@@ -21,7 +21,14 @@ syntax globallycoherent : GloballyCoherentModifier;
interface __BuiltinType {}
// A type that can be used for arithmetic operations
-interface __BuiltinArithmeticType : __BuiltinType {}
+interface __BuiltinArithmeticType : __BuiltinType
+{
+ /// Initialize from a 32-bit signed integer value.
+ __init(int value);
+}
+
+ /// A type that can be used for logical/bitwsie operations
+interface __BuiltinLogicalType : __BuiltinType {}
// A type that logically has a sign (positive/negative/zero)
interface __BuiltinSignedArithmeticType : __BuiltinArithmeticType {}
@@ -31,14 +38,16 @@ interface __BuiltinIntegerType : __BuiltinArithmeticType
{}
// A type that can represent non-integers
-interface __BuiltinRealType : __BuiltinArithmeticType {}
+interface __BuiltinRealType : __BuiltinSignedArithmeticType {}
// A type that uses a floating-point representation
-interface __BuiltinFloatingPointType : __BuiltinRealType, __BuiltinSignedArithmeticType
+interface __BuiltinFloatingPointType : __BuiltinRealType
{
- // A builtin floating-point type must have an initializer that takes
- // a floating-point value...
+ /// Initialize from a 32-bit floating-point value.
__init(float value);
+
+ /// Get the value of the mathematical constant pi in this type.
+ static This getPi();
}
// A type resulting from an `enum` declaration.
@@ -116,52 +125,58 @@ __generic<T, let N : int> __intrinsic_op(select) vector<T,N> operator?:(vector<b
${{{{
// We are going to use code generation to produce the
// declarations for all of our base types.
-
static const int kBaseTypeCount = sizeof(kBaseTypes) / sizeof(kBaseTypes[0]);
for (int tt = 0; tt < kBaseTypeCount; ++tt)
{
- EMIT_LINE_DIRECTIVE();
- sb << "__builtin_type(" << int(kBaseTypes[tt].tag) << ") struct " << kBaseTypes[tt].name;
-
- // Declare interface conformances for this type
+}}}}
- sb << "\n : __BuiltinType\n";
+__builtin_type($(int(kBaseTypes[tt].tag)))
+struct $(kBaseTypes[tt].name)
+ : __BuiltinType
+${{{{
switch (kBaseTypes[tt].tag)
{
case BaseType::Half:
case BaseType::Float:
case BaseType::Double:
- sb << "\n , __BuiltinFloatingPointType\n";
- sb << "\n , __BuiltinRealType\n";
- sb << "\n , __BuiltinSignedArithmeticType\n";
- sb << "\n , __BuiltinArithmeticType\n";
- sb << "\n , __BuiltinType\n";
+}}}}
+ , __BuiltinFloatingPointType
+ , __BuiltinRealType
+ , __BuiltinSignedArithmeticType
+ , __BuiltinArithmeticType
+${{{{
break;
case BaseType::Int8:
case BaseType::Int16:
case BaseType::Int:
case BaseType::Int64:
- sb << "\n , __BuiltinSignedArithmeticType\n";
+}}}}
+ , __BuiltinSignedArithmeticType
+${{{{
; // fall through to:
case BaseType::UInt8:
case BaseType::UInt16:
case BaseType::UInt:
case BaseType::UInt64:
- sb << "\n , __BuiltinArithmeticType\n";
- sb << "\n , __BuiltinIntegerType\n";
+}}}}
+ , __BuiltinArithmeticType
+ , __BuiltinIntegerType
+${{{{
; // fall through to:
case BaseType::Bool:
- sb << "\n , __BuiltinType\n";
+}}}}
+ , __BuiltinLogicalType
+${{{{
break;
default:
break;
}
+}}}}
+{
- sb << "\n{\n";
-
-
+${{{{
// Declare initializers to convert from various other types
for (int ss = 0; ss < kBaseTypeCount; ++ss)
{
@@ -175,12 +190,12 @@ for (int tt = 0; tt < kBaseTypeCount; ++tt)
ConversionCost conversionCost = getBaseTypeConversionCost(
kBaseTypes[tt],
kBaseTypes[ss]);
+}}}}
- EMIT_LINE_DIRECTIVE();
- sb << "__implicit_conversion(" << conversionCost << ")\n";
+ __implicit_conversion($(conversionCost))
+ __init($(kBaseTypes[ss].name) value);
- EMIT_LINE_DIRECTIVE();
- sb << "__init(" << kBaseTypes[ss].name << " value);\n";
+${{{{
}
// If this is a basic integer type, then define explicit
@@ -206,13 +221,33 @@ ${{{{
break;
}
- sb << "};\n";
+ // If this is a floating-point type, then we need to
+ // define the basic `getPi()` function that is used
+ // to implement generic versions of `degrees()` and
+ // `radians()`.
+ //
+ switch (kBaseTypes[tt].tag)
+ {
+ default:
+ break;
+ case BaseType::Half:
+ case BaseType::Float:
+ case BaseType::Double:
+}}}}
+ static $(kBaseTypes[tt].name) getPi() { return $(kBaseTypes[tt].name)(3.14159265358979323846264338328); }
+${{{{
+ break;
+ }
+}}}}
+
+}
+
+${{{{
}
// Declare built-in pointer type
// (eventually we can have the traditional syntax sugar for this)
}}}}
-
__generic<T>
__magic_type(PtrType)
__intrinsic_type($(kIROp_PtrType))
@@ -242,26 +277,31 @@ __intrinsic_type($(kIROp_StringType))
struct String
{};
-${{{{
-// Declare vector and matrix types
-
-sb << "__generic<T = float, let N : int = 4> __magic_type(Vector) struct vector\n{\n";
-sb << " typedef T Element;\n";
+ /// An `N` component vector with elements of type `T`.
+__generic<T = float, let N : int = 4>
+__magic_type(Vector)
+struct vector
+{
+ /// The element type of the vector
+ typedef T Element;
-// Declare initializer taking a single scalar of the elemnt type
-sb << " __implicit_conversion(" << kConversionCost_ScalarToVector << ")\n";
-sb << " __intrinsic_op(" << kIROp_constructVectorFromScalar << ")\n";
-sb << " __init(T value);\n";
-// Allow initialization from same type
-sb << " __init(vector<T,N> value);\n";
+ /// Initialize a vector where all elements have the same scalar `value`.
+ __implicit_conversion($(kConversionCost_ScalarToVector))
+ __intrinsic_op($(kIROp_constructVectorFromScalar))
+ __init(T value);
-sb << "};\n";
-}}}}
+ /// Initialize a vector from a value of the same type
+ // TODO: we should revise semantic checking so this kind of "identity" conversion is not required
+ __init(vector<T,N> value);
+}
+ /// A matrix with `R` rows and `C` columns, with elements of type `T`.
__generic<T = float, let R : int = 4, let C : int = 4>
__magic_type(Matrix)
-struct matrix {};
+struct matrix
+{
+}
${{{{
static const struct {
@@ -310,20 +350,24 @@ for (int tt = 0; tt < kTypeCount; ++tt)
}
// Declare additional built-in generic types
-// EMIT_LINE_DIRECTIVE();
+}}}}
+__generic<T>
+__intrinsic_type($(kIROp_ConstantBufferType))
+__magic_type(ConstantBuffer)
+struct ConstantBuffer {}
-sb << "__generic<T>\n";
-sb << "__intrinsic_type(" << kIROp_ConstantBufferType << ")\n";
-sb << "__magic_type(ConstantBuffer) struct ConstantBuffer {};\n";
+__generic<T>
+__intrinsic_type($(kIROp_TextureBufferType))
+__magic_type(TextureBuffer)
+struct TextureBuffer {}
-sb << "__generic<T>\n";
-sb << "__intrinsic_type(" << kIROp_TextureBufferType << ")\n";
-sb << "__magic_type(TextureBuffer) struct TextureBuffer {};\n";
+__generic<T>
+__intrinsic_type($(kIROp_ParameterBlockType))
+__magic_type(ParameterBlockType)
+struct ParameterBlock {}
-sb << "__generic<T>\n";
-sb << "__intrinsic_type(" << kIROp_ParameterBlockType << ")\n";
-sb << "__magic_type(ParameterBlockType) struct ParameterBlock {};\n";
+${{{{
static const char* kComponentNames[]{ "x", "y", "z", "w" };
static const char* kVectorNames[]{ "", "x", "xy", "xyz", "xyzw" };
@@ -454,20 +498,25 @@ for( int C = 2; C <= 4; ++C )
sb << "}\n";
}
-
-// Declare built-in texture and sampler types
+}}}}
+ /// Sampling state for filtered texture fetches.
+__magic_type(SamplerState, $(int(SamplerStateFlavor::SamplerState)))
+__intrinsic_type($(kIROp_SamplerStateType))
+struct SamplerState
+{
+}
-sb << "__magic_type(SamplerState," << int(SamplerStateFlavor::SamplerState) << ")\n";
-sb << "__intrinsic_type(" << kIROp_SamplerStateType << ")\n";
-sb << "struct SamplerState {};";
+ /// Sampling state for filtered texture fetches that include a comparison operation before filtering.
+__magic_type(SamplerState, $(int(SamplerStateFlavor::SamplerComparisonState)))
+__intrinsic_type($(kIROp_SamplerComparisonStateType))
+struct SamplerComparisonState
+{
+}
-sb << "__magic_type(SamplerState," << int(SamplerStateFlavor::SamplerComparisonState) << ")\n";
-sb << "__intrinsic_type(" << kIROp_SamplerComparisonStateType << ")\n";
-sb << "struct SamplerComparisonState {};";
+${{{{
-// TODO(tfoley): Need to handle `RW*` variants of texture types as well...
static const struct {
char const* name;
TextureFlavor::Shape baseShape;
@@ -1403,6 +1452,9 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
for (auto op : unaryOps)
{
+ char const* fixity = (op.flags & POSTFIX) != 0 ? "__postfix " : "__prefix ";
+ char const* qual = (op.flags & ASSIGNMENT) != 0 ? "in out " : "";
+
for (auto type : kBaseTypes)
{
if ((type.flags & op.flags) == 0)
@@ -1411,9 +1463,6 @@ for (auto op : unaryOps)
char const* resultType = type.name;
if (op.flags & BOOL_RESULT) resultType = "bool";
- char const* fixity = (op.flags & POSTFIX) != 0 ? "__postfix " : "__prefix ";
- char const* qual = (op.flags & ASSIGNMENT) != 0 ? "in out " : "";
-
// scalar version
sb << fixity;
sb << "__intrinsic_op(" << int(op.opCode) << ") " << resultType << " operator" << op.opName << "(" << qual << type.name << " value);\n";
@@ -1428,10 +1477,35 @@ for (auto op : unaryOps)
sb << fixity;
sb << "__intrinsic_op(" << int(op.opCode) << ") matrix<" << resultType << ",N,M> operator" << op.opName << "(" << qual << "matrix<" << type.name << ",N,M> value);\n";
}
+
+ // Synthesize generic versions
+ if(op.interface)
+ {
+ char const* resultType = "T";
+ if (op.flags & BOOL_RESULT) resultType = "bool";
+
+ // scalar version
+ sb << fixity;
+ sb << "__generic<T : " << op.interface << ">\n";
+ sb << "__intrinsic_op(" << int(op.opCode) << ") " << resultType << " operator" << op.opName << "(" << qual << "T value);\n";
+
+ // vector version
+ sb << "__generic<T : " << op.interface << ", let N : int> ";
+ sb << fixity;
+ sb << "__intrinsic_op(" << int(op.opCode) << ") vector<" << resultType << ",N> operator" << op.opName << "(" << qual << "vector<T,N> value);\n";
+
+ // matrix version
+ sb << "__generic<T : " << op.interface << ", let N : int, let M : int> ";
+ sb << fixity;
+ sb << "__intrinsic_op(" << int(op.opCode) << ") matrix<" << resultType << ",N,M> operator" << op.opName << "(" << qual << "matrix<T,N,M> value);\n";
+ }
}
for (auto op : binaryOps)
{
+ char const* leftQual = "";
+ if(op.flags & ASSIGNMENT) leftQual = "in out ";
+
for (auto type : kBaseTypes)
{
if ((type.flags & op.flags) == 0)
@@ -1443,10 +1517,15 @@ for (auto op : binaryOps)
if (op.flags & BOOL_RESULT) resultType = "bool";
- char const* leftQual = "";
- if(op.flags & ASSIGNMENT) leftQual = "in out ";
-
- // TODO: handle `SHIFT`
+ // TODO: We should handle a `SHIFT` flag on the op
+ // by changing `rightType` to `int` in order to
+ // account for the fact that the shift amount should
+ // always have a fixed type independent of the LHS.
+ //
+ // (It is unclear why this change hadn't been made
+ // already, so it is possible that such a change
+ // breaks overload resolution or other parts of
+ // the compiler)
// scalar version
sb << "__intrinsic_op(" << int(op.opCode) << ") " << resultType << " operator" << op.opName << "(" << leftQual << leftType << " left, " << rightType << " right);\n";
@@ -1459,9 +1538,38 @@ for (auto op : binaryOps)
sb << "__generic<let N : int, let M : int> ";
sb << "__intrinsic_op(" << int(op.opCode) << ") matrix<" << resultType << ",N,M> operator" << op.opName << "(" << leftQual << "matrix<" << leftType << ",N,M> left, matrix<" << rightType << ",N,M> right);\n";
- // We are going to go ahead and explicitly define combined
- // operations for the scalar-op-vector, etc. cases, rather
- // than rely on promotion rules.
+ // We currently synthesize addiitonal overloads
+ // for the case where one or the other operand
+ // is a scalar. This choice serves a few purposes:
+ //
+ // 1. It avoids introducing scalar-to-vector or
+ // scalar-to-matrix promotions before the operator,
+ // which might allow some back ends to produce
+ // more optimal code.
+ //
+ // 2. It avoids concerns about making overload resolution
+ // and the inference rules for `N` and `M` able to
+ // handle the mixed vector/scalar or matrix/scalar case.
+ //
+ // 3. Having explicit overloads for the matrix/scalar cases
+ // here means that we do *not* need to support a general
+ // implicit conversion from scalars to matrices, unless
+ // we decide we want to.
+ //
+ // Note: Case (2) of the motivation shouldn't really apply
+ // any more, because we end up having to support similar
+ // inteference for built-in binary math functions where
+ // vectors and scalars might be combined (and where defining
+ // additional overloads to cover all the combinations doesn't
+ // seem practical or desirable).
+ //
+ // TODO: We should consider whether dropping these extra
+ // overloads is possible and worth it. The optimization
+ // concern (1) could possibly be addressed in specific
+ // back-ends. The issue (3) about not wanting to support
+ // implicit scalar-to-matrix conversion may be moot if
+ // we end up needing to support mixed scalar/matrix input
+ // for builtin in non-operator functions anyway.
// scalar-vector and scalar-matrix
if (!(op.flags & ASSIGNMENT))
@@ -1480,6 +1588,46 @@ for (auto op : binaryOps)
sb << "__generic<let N : int, let M : int> ";
sb << "__intrinsic_op(" << int(op.opCode) << ") matrix<" << resultType << ",N,M> operator" << op.opName << "(" << leftQual << "matrix<" << leftType << ",N,M> left, " << rightType << " right);\n";
}
+
+ // Synthesize generic versions
+ if(op.interface)
+ {
+ char const* leftType = "T";
+ char const* rightType = leftType;
+ char const* resultType = leftType;
+
+ if (op.flags & BOOL_RESULT) resultType = "bool";
+ // TODO: handle `SHIFT`
+
+ // scalar version
+ sb << "__generic<T : " << op.interface << ">\n";
+ sb << "__intrinsic_op(" << int(op.opCode) << ") " << resultType << " operator" << op.opName << "(" << leftQual << leftType << " left, " << rightType << " right);\n";
+
+ // vector version
+ sb << "__generic<T : " << op.interface << ", let N : int> ";
+ sb << "__intrinsic_op(" << int(op.opCode) << ") vector<" << resultType << ",N> operator" << op.opName << "(" << leftQual << "vector<" << leftType << ",N> left, vector<" << rightType << ",N> right);\n";
+
+ // matrix version
+ sb << "__generic<T : " << op.interface << ", let N : int, let M : int> ";
+ sb << "__intrinsic_op(" << int(op.opCode) << ") matrix<" << resultType << ",N,M> operator" << op.opName << "(" << leftQual << "matrix<" << leftType << ",N,M> left, matrix<" << rightType << ",N,M> right);\n";
+
+ // scalar-vector and scalar-matrix
+ if (!(op.flags & ASSIGNMENT))
+ {
+ sb << "__generic<T : " << op.interface << ", let N : int> ";
+ sb << "__intrinsic_op(" << int(op.opCode) << ") vector<" << resultType << ",N> operator" << op.opName << "(" << leftQual << leftType << " left, vector<" << rightType << ",N> right);\n";
+
+ sb << "__generic<T : " << op.interface << ", let N : int, let M : int> ";
+ sb << "__intrinsic_op(" << int(op.opCode) << ") matrix<" << resultType << ",N,M> operator" << op.opName << "(" << leftQual << leftType << " left, matrix<" << rightType << ",N,M> right);\n";
+ }
+
+ // vector-scalar and matrix-scalar
+ sb << "__generic<T : " << op.interface << ", let N : int> ";
+ sb << "__intrinsic_op(" << int(op.opCode) << ") vector<" << resultType << ",N> operator" << op.opName << "(" << leftQual << "vector<" << leftType << ",N> left, " << rightType << " right);\n";
+
+ sb << "__generic<T : " << op.interface << ", let N : int, let M : int> ";
+ sb << "__intrinsic_op(" << int(op.opCode) << ") matrix<" << resultType << ",N,M> operator" << op.opName << "(" << leftQual << "matrix<" << leftType << ",N,M> left, " << rightType << " right);\n";
+ }
}
}}}}
diff --git a/source/slang/core.meta.slang.h b/source/slang/core.meta.slang.h
index c2ed8cfc4..f793d0c1b 100644
--- a/source/slang/core.meta.slang.h
+++ b/source/slang/core.meta.slang.h
@@ -21,7 +21,14 @@ SLANG_RAW("// A type that can be used as an operand for builtins\n")
SLANG_RAW("interface __BuiltinType {}\n")
SLANG_RAW("\n")
SLANG_RAW("// A type that can be used for arithmetic operations\n")
-SLANG_RAW("interface __BuiltinArithmeticType : __BuiltinType {}\n")
+SLANG_RAW("interface __BuiltinArithmeticType : __BuiltinType\n")
+SLANG_RAW("{\n")
+SLANG_RAW(" /// Initialize from a 32-bit signed integer value.\n")
+SLANG_RAW(" __init(int value);\n")
+SLANG_RAW("}\n")
+SLANG_RAW("\n")
+SLANG_RAW(" /// A type that can be used for logical/bitwsie operations\n")
+SLANG_RAW("interface __BuiltinLogicalType : __BuiltinType {}\n")
SLANG_RAW("\n")
SLANG_RAW("// A type that logically has a sign (positive/negative/zero)\n")
SLANG_RAW("interface __BuiltinSignedArithmeticType : __BuiltinArithmeticType {}\n")
@@ -31,14 +38,16 @@ SLANG_RAW("interface __BuiltinIntegerType : __BuiltinArithmeticType\n")
SLANG_RAW("{}\n")
SLANG_RAW("\n")
SLANG_RAW("// A type that can represent non-integers\n")
-SLANG_RAW("interface __BuiltinRealType : __BuiltinArithmeticType {}\n")
+SLANG_RAW("interface __BuiltinRealType : __BuiltinSignedArithmeticType {}\n")
SLANG_RAW("\n")
SLANG_RAW("// A type that uses a floating-point representation\n")
-SLANG_RAW("interface __BuiltinFloatingPointType : __BuiltinRealType, __BuiltinSignedArithmeticType\n")
+SLANG_RAW("interface __BuiltinFloatingPointType : __BuiltinRealType\n")
SLANG_RAW("{\n")
-SLANG_RAW(" // A builtin floating-point type must have an initializer that takes\n")
-SLANG_RAW(" // a floating-point value...\n")
+SLANG_RAW(" /// Initialize from a 32-bit floating-point value.\n")
SLANG_RAW(" __init(float value);\n")
+SLANG_RAW("\n")
+SLANG_RAW(" /// Get the value of the mathematical constant pi in this type.\n")
+SLANG_RAW(" static This getPi();\n")
SLANG_RAW("}\n")
SLANG_RAW("\n")
SLANG_RAW("// A type resulting from an `enum` declaration.\n")
@@ -119,51 +128,69 @@ SLANG_RAW("\n")
// We are going to use code generation to produce the
// declarations for all of our base types.
-
static const int kBaseTypeCount = sizeof(kBaseTypes) / sizeof(kBaseTypes[0]);
for (int tt = 0; tt < kBaseTypeCount; ++tt)
{
- EMIT_LINE_DIRECTIVE();
- sb << "__builtin_type(" << int(kBaseTypes[tt].tag) << ") struct " << kBaseTypes[tt].name;
-
- // Declare interface conformances for this type
-
- sb << "\n : __BuiltinType\n";
+SLANG_RAW("#line 131 \"core.meta.slang\"")
+SLANG_RAW("\n")
+SLANG_RAW("\n")
+SLANG_RAW("__builtin_type(")
+SLANG_SPLICE(int(kBaseTypes[tt].tag)
+)
+SLANG_RAW(")\n")
+SLANG_RAW("struct ")
+SLANG_SPLICE(kBaseTypes[tt].name
+)
+SLANG_RAW("\n")
+SLANG_RAW(" : __BuiltinType\n")
+SLANG_RAW("\n")
switch (kBaseTypes[tt].tag)
{
case BaseType::Half:
case BaseType::Float:
case BaseType::Double:
- sb << "\n , __BuiltinFloatingPointType\n";
- sb << "\n , __BuiltinRealType\n";
- sb << "\n , __BuiltinSignedArithmeticType\n";
- sb << "\n , __BuiltinArithmeticType\n";
- sb << "\n , __BuiltinType\n";
+SLANG_RAW("#line 143 \"core.meta.slang\"")
+SLANG_RAW("\n")
+SLANG_RAW(" , __BuiltinFloatingPointType\n")
+SLANG_RAW(" , __BuiltinRealType\n")
+SLANG_RAW(" , __BuiltinSignedArithmeticType\n")
+SLANG_RAW(" , __BuiltinArithmeticType\n")
+
break;
case BaseType::Int8:
case BaseType::Int16:
case BaseType::Int:
case BaseType::Int64:
- sb << "\n , __BuiltinSignedArithmeticType\n";
+SLANG_RAW("#line 154 \"core.meta.slang\"")
+SLANG_RAW("\n")
+SLANG_RAW(" , __BuiltinSignedArithmeticType\n")
+
; // fall through to:
case BaseType::UInt8:
case BaseType::UInt16:
case BaseType::UInt:
case BaseType::UInt64:
- sb << "\n , __BuiltinArithmeticType\n";
- sb << "\n , __BuiltinIntegerType\n";
+SLANG_RAW("#line 162 \"core.meta.slang\"")
+SLANG_RAW("\n")
+SLANG_RAW(" , __BuiltinArithmeticType\n")
+SLANG_RAW(" , __BuiltinIntegerType\n")
+
; // fall through to:
case BaseType::Bool:
- sb << "\n , __BuiltinType\n";
+SLANG_RAW("#line 168 \"core.meta.slang\"")
+SLANG_RAW("\n")
+SLANG_RAW(" , __BuiltinLogicalType\n")
+
break;
default:
break;
}
-
- sb << "\n{\n";
-
+SLANG_RAW("#line 176 \"core.meta.slang\"")
+SLANG_RAW("\n")
+SLANG_RAW("{\n")
+SLANG_RAW("\n")
// Declare initializers to convert from various other types
for (int ss = 0; ss < kBaseTypeCount; ++ss)
@@ -178,12 +205,19 @@ for (int tt = 0; tt < kBaseTypeCount; ++tt)
ConversionCost conversionCost = getBaseTypeConversionCost(
kBaseTypes[tt],
kBaseTypes[ss]);
+SLANG_RAW("#line 193 \"core.meta.slang\"")
+SLANG_RAW("\n")
+SLANG_RAW("\n")
+SLANG_RAW(" __implicit_conversion(")
+SLANG_SPLICE(conversionCost
+)
+SLANG_RAW(")\n")
+SLANG_RAW(" __init(")
+SLANG_SPLICE(kBaseTypes[ss].name
+)
+SLANG_RAW(" value);\n")
+SLANG_RAW("\n")
- EMIT_LINE_DIRECTIVE();
- sb << "__implicit_conversion(" << conversionCost << ")\n";
-
- EMIT_LINE_DIRECTIVE();
- sb << "__init(" << kBaseTypes[ss].name << " value);\n";
}
// If this is a basic integer type, then define explicit
@@ -199,7 +233,7 @@ for (int tt = 0; tt < kBaseTypeCount; ++tt)
// TODO: should this cover the full gamut of integer types?
case BaseType::Int:
case BaseType::UInt:
-SLANG_RAW("#line 199 \"core.meta.slang\"")
+SLANG_RAW("#line 214 \"core.meta.slang\"")
SLANG_RAW("\n")
SLANG_RAW(" __generic<T:__EnumType>\n")
SLANG_RAW(" __init(T value);\n")
@@ -210,13 +244,41 @@ SLANG_RAW(" __init(T value);\n")
break;
}
- sb << "};\n";
+ // If this is a floating-point type, then we need to
+ // define the basic `getPi()` function that is used
+ // to implement generic versions of `degrees()` and
+ // `radians()`.
+ //
+ switch (kBaseTypes[tt].tag)
+ {
+ default:
+ break;
+ case BaseType::Half:
+ case BaseType::Float:
+ case BaseType::Double:
+SLANG_RAW("#line 236 \"core.meta.slang\"")
+SLANG_RAW("\n")
+SLANG_RAW(" static ")
+SLANG_SPLICE(kBaseTypes[tt].name
+)
+SLANG_RAW(" getPi() { return ")
+SLANG_SPLICE(kBaseTypes[tt].name
+)
+SLANG_RAW("(3.14159265358979323846264338328); }\n")
+
+ break;
+ }
+SLANG_RAW("#line 241 \"core.meta.slang\"")
+SLANG_RAW("\n")
+SLANG_RAW("\n")
+SLANG_RAW("}\n")
+SLANG_RAW("\n")
+
}
// Declare built-in pointer type
// (eventually we can have the traditional syntax sugar for this)
-SLANG_RAW("#line 214 \"core.meta.slang\"")
-SLANG_RAW("\n")
+SLANG_RAW("#line 250 \"core.meta.slang\"")
SLANG_RAW("\n")
SLANG_RAW("__generic<T>\n")
SLANG_RAW("__magic_type(PtrType)\n")
@@ -262,27 +324,37 @@ SLANG_RAW(")\n")
SLANG_RAW("struct String\n")
SLANG_RAW("{};\n")
SLANG_RAW("\n")
-
-// Declare vector and matrix types
-
-sb << "__generic<T = float, let N : int = 4> __magic_type(Vector) struct vector\n{\n";
-sb << " typedef T Element;\n";
-
-// Declare initializer taking a single scalar of the elemnt type
-sb << " __implicit_conversion(" << kConversionCost_ScalarToVector << ")\n";
-sb << " __intrinsic_op(" << kIROp_constructVectorFromScalar << ")\n";
-sb << " __init(T value);\n";
-
-// Allow initialization from same type
-sb << " __init(vector<T,N> value);\n";
-
-sb << "};\n";
-SLANG_RAW("#line 260 \"core.meta.slang\"")
+SLANG_RAW(" /// An `N` component vector with elements of type `T`.\n")
+SLANG_RAW("__generic<T = float, let N : int = 4>\n")
+SLANG_RAW("__magic_type(Vector)\n")
+SLANG_RAW("struct vector\n")
+SLANG_RAW("{\n")
+SLANG_RAW(" /// The element type of the vector\n")
+SLANG_RAW(" typedef T Element;\n")
+SLANG_RAW("\n")
+SLANG_RAW("\n")
+SLANG_RAW(" /// Initialize a vector where all elements have the same scalar `value`.\n")
+SLANG_RAW(" __implicit_conversion(")
+SLANG_SPLICE(kConversionCost_ScalarToVector
+)
+SLANG_RAW(")\n")
+SLANG_RAW(" __intrinsic_op(")
+SLANG_SPLICE(kIROp_constructVectorFromScalar
+)
+SLANG_RAW(")\n")
+SLANG_RAW(" __init(T value);\n")
SLANG_RAW("\n")
+SLANG_RAW(" /// Initialize a vector from a value of the same type\n")
+SLANG_RAW(" // TODO: we should revise semantic checking so this kind of \"identity\" conversion is not required\n")
+SLANG_RAW(" __init(vector<T,N> value);\n")
+SLANG_RAW("}\n")
SLANG_RAW("\n")
+SLANG_RAW(" /// A matrix with `R` rows and `C` columns, with elements of type `T`.\n")
SLANG_RAW("__generic<T = float, let R : int = 4, let C : int = 4>\n")
SLANG_RAW("__magic_type(Matrix)\n")
-SLANG_RAW("struct matrix {};\n")
+SLANG_RAW("struct matrix\n")
+SLANG_RAW("{\n")
+SLANG_RAW("}\n")
SLANG_RAW("\n")
static const struct {
@@ -331,20 +403,34 @@ for (int tt = 0; tt < kTypeCount; ++tt)
}
// Declare additional built-in generic types
-// EMIT_LINE_DIRECTIVE();
-
-
-sb << "__generic<T>\n";
-sb << "__intrinsic_type(" << kIROp_ConstantBufferType << ")\n";
-sb << "__magic_type(ConstantBuffer) struct ConstantBuffer {};\n";
-
-sb << "__generic<T>\n";
-sb << "__intrinsic_type(" << kIROp_TextureBufferType << ")\n";
-sb << "__magic_type(TextureBuffer) struct TextureBuffer {};\n";
+SLANG_RAW("#line 353 \"core.meta.slang\"")
+SLANG_RAW("\n")
+SLANG_RAW("\n")
+SLANG_RAW("__generic<T>\n")
+SLANG_RAW("__intrinsic_type(")
+SLANG_SPLICE(kIROp_ConstantBufferType
+)
+SLANG_RAW(")\n")
+SLANG_RAW("__magic_type(ConstantBuffer)\n")
+SLANG_RAW("struct ConstantBuffer {}\n")
+SLANG_RAW("\n")
+SLANG_RAW("__generic<T>\n")
+SLANG_RAW("__intrinsic_type(")
+SLANG_SPLICE(kIROp_TextureBufferType
+)
+SLANG_RAW(")\n")
+SLANG_RAW("__magic_type(TextureBuffer)\n")
+SLANG_RAW("struct TextureBuffer {}\n")
+SLANG_RAW("\n")
+SLANG_RAW("__generic<T>\n")
+SLANG_RAW("__intrinsic_type(")
+SLANG_SPLICE(kIROp_ParameterBlockType
+)
+SLANG_RAW(")\n")
+SLANG_RAW("__magic_type(ParameterBlockType)\n")
+SLANG_RAW("struct ParameterBlock {}\n")
+SLANG_RAW("\n")
-sb << "__generic<T>\n";
-sb << "__intrinsic_type(" << kIROp_ParameterBlockType << ")\n";
-sb << "__magic_type(ParameterBlockType) struct ParameterBlock {};\n";
static const char* kComponentNames[]{ "x", "y", "z", "w" };
static const char* kVectorNames[]{ "", "x", "xy", "xyz", "xyzw" };
@@ -475,20 +561,38 @@ for( int C = 2; C <= 4; ++C )
sb << "}\n";
}
+SLANG_RAW("#line 501 \"core.meta.slang\"")
+SLANG_RAW("\n")
+SLANG_RAW("\n")
+SLANG_RAW("\n")
+SLANG_RAW(" /// Sampling state for filtered texture fetches.\n")
+SLANG_RAW("__magic_type(SamplerState, ")
+SLANG_SPLICE(int(SamplerStateFlavor::SamplerState)
+)
+SLANG_RAW(")\n")
+SLANG_RAW("__intrinsic_type(")
+SLANG_SPLICE(kIROp_SamplerStateType
+)
+SLANG_RAW(")\n")
+SLANG_RAW("struct SamplerState\n")
+SLANG_RAW("{\n")
+SLANG_RAW("}\n")
+SLANG_RAW("\n")
+SLANG_RAW(" /// Sampling state for filtered texture fetches that include a comparison operation before filtering.\n")
+SLANG_RAW("__magic_type(SamplerState, ")
+SLANG_SPLICE(int(SamplerStateFlavor::SamplerComparisonState)
+)
+SLANG_RAW(")\n")
+SLANG_RAW("__intrinsic_type(")
+SLANG_SPLICE(kIROp_SamplerComparisonStateType
+)
+SLANG_RAW(")\n")
+SLANG_RAW("struct SamplerComparisonState\n")
+SLANG_RAW("{\n")
+SLANG_RAW("}\n")
+SLANG_RAW("\n")
-// Declare built-in texture and sampler types
-
-
-
-sb << "__magic_type(SamplerState," << int(SamplerStateFlavor::SamplerState) << ")\n";
-sb << "__intrinsic_type(" << kIROp_SamplerStateType << ")\n";
-sb << "struct SamplerState {};";
-
-sb << "__magic_type(SamplerState," << int(SamplerStateFlavor::SamplerComparisonState) << ")\n";
-sb << "__intrinsic_type(" << kIROp_SamplerComparisonStateType << ")\n";
-sb << "struct SamplerComparisonState {};";
-// TODO(tfoley): Need to handle `RW*` variants of texture types as well...
static const struct {
char const* name;
TextureFlavor::Shape baseShape;
@@ -1424,6 +1528,9 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
for (auto op : unaryOps)
{
+ char const* fixity = (op.flags & POSTFIX) != 0 ? "__postfix " : "__prefix ";
+ char const* qual = (op.flags & ASSIGNMENT) != 0 ? "in out " : "";
+
for (auto type : kBaseTypes)
{
if ((type.flags & op.flags) == 0)
@@ -1432,9 +1539,6 @@ for (auto op : unaryOps)
char const* resultType = type.name;
if (op.flags & BOOL_RESULT) resultType = "bool";
- char const* fixity = (op.flags & POSTFIX) != 0 ? "__postfix " : "__prefix ";
- char const* qual = (op.flags & ASSIGNMENT) != 0 ? "in out " : "";
-
// scalar version
sb << fixity;
sb << "__intrinsic_op(" << int(op.opCode) << ") " << resultType << " operator" << op.opName << "(" << qual << type.name << " value);\n";
@@ -1449,10 +1553,35 @@ for (auto op : unaryOps)
sb << fixity;
sb << "__intrinsic_op(" << int(op.opCode) << ") matrix<" << resultType << ",N,M> operator" << op.opName << "(" << qual << "matrix<" << type.name << ",N,M> value);\n";
}
+
+ // Synthesize generic versions
+ if(op.interface)
+ {
+ char const* resultType = "T";
+ if (op.flags & BOOL_RESULT) resultType = "bool";
+
+ // scalar version
+ sb << fixity;
+ sb << "__generic<T : " << op.interface << ">\n";
+ sb << "__intrinsic_op(" << int(op.opCode) << ") " << resultType << " operator" << op.opName << "(" << qual << "T value);\n";
+
+ // vector version
+ sb << "__generic<T : " << op.interface << ", let N : int> ";
+ sb << fixity;
+ sb << "__intrinsic_op(" << int(op.opCode) << ") vector<" << resultType << ",N> operator" << op.opName << "(" << qual << "vector<T,N> value);\n";
+
+ // matrix version
+ sb << "__generic<T : " << op.interface << ", let N : int, let M : int> ";
+ sb << fixity;
+ sb << "__intrinsic_op(" << int(op.opCode) << ") matrix<" << resultType << ",N,M> operator" << op.opName << "(" << qual << "matrix<T,N,M> value);\n";
+ }
}
for (auto op : binaryOps)
{
+ char const* leftQual = "";
+ if(op.flags & ASSIGNMENT) leftQual = "in out ";
+
for (auto type : kBaseTypes)
{
if ((type.flags & op.flags) == 0)
@@ -1464,10 +1593,15 @@ for (auto op : binaryOps)
if (op.flags & BOOL_RESULT) resultType = "bool";
- char const* leftQual = "";
- if(op.flags & ASSIGNMENT) leftQual = "in out ";
-
- // TODO: handle `SHIFT`
+ // TODO: We should handle a `SHIFT` flag on the op
+ // by changing `rightType` to `int` in order to
+ // account for the fact that the shift amount should
+ // always have a fixed type independent of the LHS.
+ //
+ // (It is unclear why this change hadn't been made
+ // already, so it is possible that such a change
+ // breaks overload resolution or other parts of
+ // the compiler)
// scalar version
sb << "__intrinsic_op(" << int(op.opCode) << ") " << resultType << " operator" << op.opName << "(" << leftQual << leftType << " left, " << rightType << " right);\n";
@@ -1480,9 +1614,38 @@ for (auto op : binaryOps)
sb << "__generic<let N : int, let M : int> ";
sb << "__intrinsic_op(" << int(op.opCode) << ") matrix<" << resultType << ",N,M> operator" << op.opName << "(" << leftQual << "matrix<" << leftType << ",N,M> left, matrix<" << rightType << ",N,M> right);\n";
- // We are going to go ahead and explicitly define combined
- // operations for the scalar-op-vector, etc. cases, rather
- // than rely on promotion rules.
+ // We currently synthesize addiitonal overloads
+ // for the case where one or the other operand
+ // is a scalar. This choice serves a few purposes:
+ //
+ // 1. It avoids introducing scalar-to-vector or
+ // scalar-to-matrix promotions before the operator,
+ // which might allow some back ends to produce
+ // more optimal code.
+ //
+ // 2. It avoids concerns about making overload resolution
+ // and the inference rules for `N` and `M` able to
+ // handle the mixed vector/scalar or matrix/scalar case.
+ //
+ // 3. Having explicit overloads for the matrix/scalar cases
+ // here means that we do *not* need to support a general
+ // implicit conversion from scalars to matrices, unless
+ // we decide we want to.
+ //
+ // Note: Case (2) of the motivation shouldn't really apply
+ // any more, because we end up having to support similar
+ // inteference for built-in binary math functions where
+ // vectors and scalars might be combined (and where defining
+ // additional overloads to cover all the combinations doesn't
+ // seem practical or desirable).
+ //
+ // TODO: We should consider whether dropping these extra
+ // overloads is possible and worth it. The optimization
+ // concern (1) could possibly be addressed in specific
+ // back-ends. The issue (3) about not wanting to support
+ // implicit scalar-to-matrix conversion may be moot if
+ // we end up needing to support mixed scalar/matrix input
+ // for builtin in non-operator functions anyway.
// scalar-vector and scalar-matrix
if (!(op.flags & ASSIGNMENT))
@@ -1501,8 +1664,48 @@ for (auto op : binaryOps)
sb << "__generic<let N : int, let M : int> ";
sb << "__intrinsic_op(" << int(op.opCode) << ") matrix<" << resultType << ",N,M> operator" << op.opName << "(" << leftQual << "matrix<" << leftType << ",N,M> left, " << rightType << " right);\n";
}
+
+ // Synthesize generic versions
+ if(op.interface)
+ {
+ char const* leftType = "T";
+ char const* rightType = leftType;
+ char const* resultType = leftType;
+
+ if (op.flags & BOOL_RESULT) resultType = "bool";
+ // TODO: handle `SHIFT`
+
+ // scalar version
+ sb << "__generic<T : " << op.interface << ">\n";
+ sb << "__intrinsic_op(" << int(op.opCode) << ") " << resultType << " operator" << op.opName << "(" << leftQual << leftType << " left, " << rightType << " right);\n";
+
+ // vector version
+ sb << "__generic<T : " << op.interface << ", let N : int> ";
+ sb << "__intrinsic_op(" << int(op.opCode) << ") vector<" << resultType << ",N> operator" << op.opName << "(" << leftQual << "vector<" << leftType << ",N> left, vector<" << rightType << ",N> right);\n";
+
+ // matrix version
+ sb << "__generic<T : " << op.interface << ", let N : int, let M : int> ";
+ sb << "__intrinsic_op(" << int(op.opCode) << ") matrix<" << resultType << ",N,M> operator" << op.opName << "(" << leftQual << "matrix<" << leftType << ",N,M> left, matrix<" << rightType << ",N,M> right);\n";
+
+ // scalar-vector and scalar-matrix
+ if (!(op.flags & ASSIGNMENT))
+ {
+ sb << "__generic<T : " << op.interface << ", let N : int> ";
+ sb << "__intrinsic_op(" << int(op.opCode) << ") vector<" << resultType << ",N> operator" << op.opName << "(" << leftQual << leftType << " left, vector<" << rightType << ",N> right);\n";
+
+ sb << "__generic<T : " << op.interface << ", let N : int, let M : int> ";
+ sb << "__intrinsic_op(" << int(op.opCode) << ") matrix<" << resultType << ",N,M> operator" << op.opName << "(" << leftQual << leftType << " left, matrix<" << rightType << ",N,M> right);\n";
+ }
+
+ // vector-scalar and matrix-scalar
+ sb << "__generic<T : " << op.interface << ", let N : int> ";
+ sb << "__intrinsic_op(" << int(op.opCode) << ") vector<" << resultType << ",N> operator" << op.opName << "(" << leftQual << "vector<" << leftType << ",N> left, " << rightType << " right);\n";
+
+ sb << "__generic<T : " << op.interface << ", let N : int, let M : int> ";
+ sb << "__intrinsic_op(" << int(op.opCode) << ") matrix<" << resultType << ",N,M> operator" << op.opName << "(" << leftQual << "matrix<" << leftType << ",N,M> left, " << rightType << " right);\n";
+ }
}
-SLANG_RAW("#line 1484 \"core.meta.slang\"")
+SLANG_RAW("#line 1632 \"core.meta.slang\"")
SLANG_RAW("\n")
SLANG_RAW("\n")
SLANG_RAW("// Specialized function\n")
diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang
index de81cd9f8..b020ef4d4 100644
--- a/source/slang/hlsl.meta.slang
+++ b/source/slang/hlsl.meta.slang
@@ -350,6 +350,9 @@ void abort();
__generic<T : __BuiltinSignedArithmeticType>
T abs(T x);
+/*{
+ return x < 0 ? -x : x;
+}*/
__generic<T : __BuiltinSignedArithmeticType, let N : int>
__target_intrinsic(hlsl)
@@ -387,9 +390,37 @@ matrix<T, N, M> acos(matrix<T, N, M> x)
}
// Test if all components are non-zero (HLSL SM 1.0)
-__generic<T : __BuiltinType> bool all(T x);
-__generic<T : __BuiltinType, let N : int> bool all(vector<T,N> x);
-__generic<T : __BuiltinType, let N : int, let M : int> bool all(matrix<T,N,M> x);
+__generic<T : __BuiltinType>
+__target_intrinsic(glsl, "bool($0)")
+bool all(T x);
+
+__generic<T : __BuiltinType, let N : int>
+__target_intrinsic(hlsl)
+__target_intrinsic(glsl, "all(bvec$N0($0))")
+bool all(vector<T,N> x);
+// TODO: implementation of `all()` in the stdlib is
+// blocked on fixing implementation of `bool` vector
+// `getAt` on the CUDA codegen path.
+/*
+{
+ bool result = true;
+ for(int i = 0; i < N; ++i)
+ result = result && all(x[i]);
+ return result;
+}
+*/
+
+__generic<T : __BuiltinType, let N : int, let M : int>
+__target_intrinsic(hlsl)
+bool all(matrix<T,N,M> x);
+/*
+{
+ bool result = true;
+ for(int i = 0; i < N; ++i)
+ result = result && all(x[i]);
+ return result;
+}
+*/
// Barrier for writes to all memory spaces (HLSL SM 5.0)
__target_intrinsic(glsl, "memoryBarrier(), groupMemoryBarrier(), memoryBarrierImage(), memoryBarrierBuffer()")
@@ -715,27 +746,24 @@ matrix<T,N,M> clamp(matrix<T,N,M> x, matrix<T,N,M> minBound, matrix<T,N,M> maxBo
// Clip (discard) fragment conditionally
__generic<T : __BuiltinFloatingPointType>
__target_intrinsic(hlsl)
-void clip(T x);
-// TODO: filling this in here requires ability to invoke `operator<(T,T)`
-/*{
+void clip(T x)
+{
if(x < T(0)) discard;
-}*/
+}
__generic<T : __BuiltinFloatingPointType, let N : int>
__target_intrinsic(hlsl)
-void clip(vector<T,N> x);
-// TODO: filling this in here requires ability to invoke `operator<(T,T)`
-/*{
+void clip(vector<T,N> x)
+{
if(any(x < T(0))) discard;
-}*/
+}
__generic<T : __BuiltinFloatingPointType, let N : int, let M : int>
__target_intrinsic(hlsl)
-void clip(matrix<T,N,M> x);
-// TODO: filling this in here requires ability to invoke `operator<(T,T)`
-/*{
+void clip(matrix<T,N,M> x)
+{
if(any(x < T(0))) discard;
-}*/
+}
// Cosine
__generic<T : __BuiltinFloatingPointType>
@@ -783,15 +811,13 @@ uint countbits(uint value);
__generic<T : __BuiltinArithmeticType>
__target_intrinsic(hlsl)
__target_intrinsic(glsl)
-vector<T,3> cross(vector<T,3> left, vector<T,3> right);
-// TODO: filling this in here requires ability to invoke `operator*(T,T)`, etc.
-/*{
+vector<T,3> cross(vector<T,3> left, vector<T,3> right)
+{
return vector<T,3>(
left.y * right.z - left.z * right.y,
left.z * right.x - left.x * right.z,
left.x * right.y - left.y * right.x);
-}*/
-
+}
// Convert encoded color
__target_intrinsic(hlsl)
@@ -807,54 +833,83 @@ __target_intrinsic(glsl, dFdx)
T ddx(T x);
__generic<T : __BuiltinFloatingPointType, let N : int>
+__target_intrinsic(hlsl)
__target_intrinsic(glsl, dFdx)
-vector<T,N> ddx(vector<T,N> x);
+vector<T, N> ddx(vector<T, N> x)
+{
+ VECTOR_MAP_UNARY(T, N, ddx, x);
+}
__generic<T : __BuiltinFloatingPointType, let N : int, let M : int>
-__target_intrinsic(glsl, dFdx)
-matrix<T,N,M> ddx(matrix<T,N,M> x);
+__target_intrinsic(hlsl)
+matrix<T, N, M> ddx(matrix<T, N, M> x)
+{
+ MATRIX_MAP_UNARY(T, N, M, ddx, x);
+}
__generic<T : __BuiltinFloatingPointType>
+__target_intrinsic(hlsl)
__glsl_extension(GL_ARB_derivative_control)
__target_intrinsic(glsl, dFdxCoarse)
T ddx_coarse(T x);
__generic<T : __BuiltinFloatingPointType, let N : int>
+__target_intrinsic(hlsl)
__glsl_extension(GL_ARB_derivative_control)
__target_intrinsic(glsl, dFdxCoarse)
-vector<T,N> ddx_coarse(vector<T,N> x);
+vector<T, N> ddx_coarse(vector<T, N> x)
+{
+ VECTOR_MAP_UNARY(T, N, ddx_coarse, x);
+}
__generic<T : __BuiltinFloatingPointType, let N : int, let M : int>
-__glsl_extension(GL_ARB_derivative_control)
-__target_intrinsic(glsl, dFdxCoarse)
-matrix<T,N,M> ddx_coarse(matrix<T,N,M> x);
+__target_intrinsic(hlsl)
+matrix<T, N, M> ddx_coarse(matrix<T, N, M> x)
+{
+ MATRIX_MAP_UNARY(T, N, M, ddx_coarse, x);
+}
__generic<T : __BuiltinFloatingPointType>
+__target_intrinsic(hlsl)
__glsl_extension(GL_ARB_derivative_control)
__target_intrinsic(glsl, dFdxFine)
T ddx_fine(T x);
__generic<T : __BuiltinFloatingPointType, let N : int>
+__target_intrinsic(hlsl)
__glsl_extension(GL_ARB_derivative_control)
__target_intrinsic(glsl, dFdxFine)
-vector<T,N> ddx_fine(vector<T,N> x);
+vector<T, N> ddx_fine(vector<T, N> x)
+{
+ VECTOR_MAP_UNARY(T, N, ddx_fine, x);
+}
__generic<T : __BuiltinFloatingPointType, let N : int, let M : int>
-__glsl_extension(GL_ARB_derivative_control)
-__target_intrinsic(glsl, dFdxFine)
-matrix<T,N,M> ddx_fine(matrix<T,N,M> x);
+__target_intrinsic(hlsl)
+matrix<T, N, M> ddx_fine(matrix<T, N, M> x)
+{
+ MATRIX_MAP_UNARY(T, N, M, ddx_fine, x);
+}
__generic<T : __BuiltinFloatingPointType>
+__target_intrinsic(hlsl)
__target_intrinsic(glsl, dFdy)
T ddy(T x);
__generic<T : __BuiltinFloatingPointType, let N : int>
+__target_intrinsic(hlsl)
__target_intrinsic(glsl, dFdy)
-vector<T,N> ddy(vector<T,N> x);
+vector<T, N> ddy(vector<T, N> x)
+{
+ VECTOR_MAP_UNARY(T, N, ddy, x);
+}
__generic<T : __BuiltinFloatingPointType, let N : int, let M : int>
-__target_intrinsic(glsl, dFdy)
- matrix<T,N,M> ddy(matrix<T,N,M> x);
+__target_intrinsic(hlsl)
+matrix<T, N, M> ddy(matrix<T, N, M> x)
+{
+ MATRIX_MAP_UNARY(T, N, M, ddy, x);
+}
__generic<T : __BuiltinFloatingPointType>
__glsl_extension(GL_ARB_derivative_control)
@@ -862,44 +917,61 @@ __target_intrinsic(glsl, dFdyCoarse)
T ddy_coarse(T x);
__generic<T : __BuiltinFloatingPointType, let N : int>
+__target_intrinsic(hlsl)
__glsl_extension(GL_ARB_derivative_control)
__target_intrinsic(glsl, dFdyCoarse)
-vector<T,N> ddy_coarse(vector<T,N> x);
+vector<T, N> ddy_coarse(vector<T, N> x)
+{
+ VECTOR_MAP_UNARY(T, N, ddy_coarse, x);
+}
__generic<T : __BuiltinFloatingPointType, let N : int, let M : int>
-__glsl_extension(GL_ARB_derivative_control)
-__target_intrinsic(glsl, dFdyCoarse)
-matrix<T,N,M> ddy_coarse(matrix<T,N,M> x);
+__target_intrinsic(hlsl)
+matrix<T, N, M> ddy_coarse(matrix<T, N, M> x)
+{
+ MATRIX_MAP_UNARY(T, N, M, ddy_coarse, x);
+}
__generic<T : __BuiltinFloatingPointType>
+__target_intrinsic(hlsl)
__glsl_extension(GL_ARB_derivative_control)
__target_intrinsic(glsl, dFdyFine)
T ddy_fine(T x);
__generic<T : __BuiltinFloatingPointType, let N : int>
+__target_intrinsic(hlsl)
__glsl_extension(GL_ARB_derivative_control)
__target_intrinsic(glsl, dFdyFine)
-vector<T,N> ddy_fine(vector<T,N> x);
+vector<T, N> ddy_fine(vector<T, N> x)
+{
+ VECTOR_MAP_UNARY(T, N, ddy_fine, x);
+}
__generic<T : __BuiltinFloatingPointType, let N : int, let M : int>
-__glsl_extension(GL_ARB_derivative_control)
-__target_intrinsic(glsl, dFdyFine)
-matrix<T,N,M> ddy_fine(matrix<T,N,M> x);
+__target_intrinsic(hlsl)
+matrix<T, N, M> ddy_fine(matrix<T, N, M> x)
+{
+ MATRIX_MAP_UNARY(T, N, M, ddy_fine, x);
+}
// Radians to degrees
__generic<T : __BuiltinFloatingPointType>
__target_intrinsic(hlsl)
-__target_intrinsic(hlsl)
-T degrees(T x);
-// TODO: filling this in here requires ability to invoke `operator*` on T,
-// and convert a constant to `T` for the conversion factor
+__target_intrinsic(glsl)
+T degrees(T x)
+{
+ return x * (T(180) / T.getPi());
+}
__generic<T : __BuiltinFloatingPointType, let N : int>
__target_intrinsic(hlsl)
-__target_intrinsic(hlsl)
-vector<T,N> degrees(vector<T,N> x);
+__target_intrinsic(glsl)
+vector<T, N> degrees(vector<T, N> x)
+{
+ VECTOR_MAP_UNARY(T, N, degrees, x);
+}
__generic<T : __BuiltinFloatingPointType, let N : int, let M : int>
__target_intrinsic(hlsl)
@@ -923,11 +995,26 @@ void DeviceMemoryBarrierWithGroupSync();
// Vector distance
-__generic<T : __BuiltinFloatingPointType, let N : int> T distance(vector<T,N> x, vector<T,N> y);
+__generic<T : __BuiltinFloatingPointType, let N : int>
+__target_intrinsic(hlsl)
+__target_intrinsic(glsl)
+T distance(vector<T, N> x, vector<T, N> y)
+{
+ return length(x - y);
+}
// Vector dot product
-__generic<T : __BuiltinArithmeticType, let N : int> T dot(vector<T,N> x, vector<T,N> y);
+__generic<T : __BuiltinArithmeticType, let N : int>
+__target_intrinsic(hlsl)
+__target_intrinsic(glsl)
+T dot(vector<T, N> x, vector<T, N> y)
+{
+ T result = T(0);
+ for(int i = 0; i < N; ++i)
+ result += x[i] * y[i];
+ return result;
+}
// Helper for computing distance terms for lighting (obsolete)
@@ -1040,10 +1127,10 @@ vector<uint,N> f32tof16(vector<float,N> value);
__generic<T : __BuiltinFloatingPointType, let N : int>
__target_intrinsic(hlsl)
__target_intrinsic(glsl)
-vector<T,N> faceforward(vector<T,N> n, vector<T,N> i, vector<T,N> ng);
-/*{
+vector<T,N> faceforward(vector<T,N> n, vector<T,N> i, vector<T,N> ng)
+{
return dot(ng, i) < T(0.0f) ? n : -n;
-}*/
+}
// Find first set bit starting at high bit and working down
__target_intrinsic(glsl,"findMSB")
diff --git a/source/slang/hlsl.meta.slang.h b/source/slang/hlsl.meta.slang.h
index 17d7aa0bc..677fa48f1 100644
--- a/source/slang/hlsl.meta.slang.h
+++ b/source/slang/hlsl.meta.slang.h
@@ -399,6 +399,9 @@ SLANG_RAW("// Absolute value (HLSL SM 1.0)\n")
SLANG_RAW("\n")
SLANG_RAW("__generic<T : __BuiltinSignedArithmeticType>\n")
SLANG_RAW("T abs(T x);\n")
+SLANG_RAW("/*{\n")
+SLANG_RAW(" return x < 0 ? -x : x;\n")
+SLANG_RAW("}*/\n")
SLANG_RAW("\n")
SLANG_RAW("__generic<T : __BuiltinSignedArithmeticType, let N : int>\n")
SLANG_RAW("__target_intrinsic(hlsl)\n")
@@ -436,9 +439,37 @@ SLANG_RAW(" MATRIX_MAP_UNARY(T, N, M, acos, x);\n")
SLANG_RAW("}\n")
SLANG_RAW("\n")
SLANG_RAW("// Test if all components are non-zero (HLSL SM 1.0)\n")
-SLANG_RAW("__generic<T : __BuiltinType> bool all(T x);\n")
-SLANG_RAW("__generic<T : __BuiltinType, let N : int> bool all(vector<T,N> x);\n")
-SLANG_RAW("__generic<T : __BuiltinType, let N : int, let M : int> bool all(matrix<T,N,M> x);\n")
+SLANG_RAW("__generic<T : __BuiltinType>\n")
+SLANG_RAW("__target_intrinsic(glsl, \"bool($0)\")\n")
+SLANG_RAW("bool all(T x);\n")
+SLANG_RAW("\n")
+SLANG_RAW("__generic<T : __BuiltinType, let N : int>\n")
+SLANG_RAW("__target_intrinsic(hlsl)\n")
+SLANG_RAW("__target_intrinsic(glsl, \"all(bvec$N0($0))\")\n")
+SLANG_RAW("bool all(vector<T,N> x);\n")
+SLANG_RAW("// TODO: implementation of `all()` in the stdlib is\n")
+SLANG_RAW("// blocked on fixing implementation of `bool` vector\n")
+SLANG_RAW("// `getAt` on the CUDA codegen path.\n")
+SLANG_RAW("/*\n")
+SLANG_RAW("{\n")
+SLANG_RAW(" bool result = true;\n")
+SLANG_RAW(" for(int i = 0; i < N; ++i)\n")
+SLANG_RAW(" result = result && all(x[i]);\n")
+SLANG_RAW(" return result;\n")
+SLANG_RAW("}\n")
+SLANG_RAW("*/\n")
+SLANG_RAW("\n")
+SLANG_RAW("__generic<T : __BuiltinType, let N : int, let M : int>\n")
+SLANG_RAW("__target_intrinsic(hlsl)\n")
+SLANG_RAW("bool all(matrix<T,N,M> x);\n")
+SLANG_RAW("/*\n")
+SLANG_RAW("{\n")
+SLANG_RAW(" bool result = true;\n")
+SLANG_RAW(" for(int i = 0; i < N; ++i)\n")
+SLANG_RAW(" result = result && all(x[i]);\n")
+SLANG_RAW(" return result;\n")
+SLANG_RAW("}\n")
+SLANG_RAW("*/\n")
SLANG_RAW("\n")
SLANG_RAW("// Barrier for writes to all memory spaces (HLSL SM 5.0)\n")
SLANG_RAW("__target_intrinsic(glsl, \"memoryBarrier(), groupMemoryBarrier(), memoryBarrierImage(), memoryBarrierBuffer()\")\n")
@@ -791,27 +822,24 @@ SLANG_RAW("\n")
SLANG_RAW("// Clip (discard) fragment conditionally\n")
SLANG_RAW("__generic<T : __BuiltinFloatingPointType>\n")
SLANG_RAW("__target_intrinsic(hlsl)\n")
-SLANG_RAW("void clip(T x);\n")
-SLANG_RAW("// TODO: filling this in here requires ability to invoke `operator<(T,T)`\n")
-SLANG_RAW("/*{\n")
+SLANG_RAW("void clip(T x)\n")
+SLANG_RAW("{\n")
SLANG_RAW(" if(x < T(0)) discard;\n")
-SLANG_RAW("}*/\n")
+SLANG_RAW("}\n")
SLANG_RAW("\n")
SLANG_RAW("__generic<T : __BuiltinFloatingPointType, let N : int>\n")
SLANG_RAW("__target_intrinsic(hlsl)\n")
-SLANG_RAW("void clip(vector<T,N> x);\n")
-SLANG_RAW("// TODO: filling this in here requires ability to invoke `operator<(T,T)`\n")
-SLANG_RAW("/*{\n")
+SLANG_RAW("void clip(vector<T,N> x)\n")
+SLANG_RAW("{\n")
SLANG_RAW(" if(any(x < T(0))) discard;\n")
-SLANG_RAW("}*/\n")
+SLANG_RAW("}\n")
SLANG_RAW("\n")
SLANG_RAW("__generic<T : __BuiltinFloatingPointType, let N : int, let M : int>\n")
SLANG_RAW("__target_intrinsic(hlsl)\n")
-SLANG_RAW("void clip(matrix<T,N,M> x);\n")
-SLANG_RAW("// TODO: filling this in here requires ability to invoke `operator<(T,T)`\n")
-SLANG_RAW("/*{\n")
+SLANG_RAW("void clip(matrix<T,N,M> x)\n")
+SLANG_RAW("{\n")
SLANG_RAW(" if(any(x < T(0))) discard;\n")
-SLANG_RAW("}*/\n")
+SLANG_RAW("}\n")
SLANG_RAW("\n")
SLANG_RAW("// Cosine\n")
SLANG_RAW("__generic<T : __BuiltinFloatingPointType>\n")
@@ -859,15 +887,13 @@ SLANG_RAW("// Cross product\n")
SLANG_RAW("__generic<T : __BuiltinArithmeticType>\n")
SLANG_RAW("__target_intrinsic(hlsl)\n")
SLANG_RAW("__target_intrinsic(glsl)\n")
-SLANG_RAW("vector<T,3> cross(vector<T,3> left, vector<T,3> right);\n")
-SLANG_RAW("// TODO: filling this in here requires ability to invoke `operator*(T,T)`, etc.\n")
-SLANG_RAW("/*{\n")
+SLANG_RAW("vector<T,3> cross(vector<T,3> left, vector<T,3> right)\n")
+SLANG_RAW("{\n")
SLANG_RAW(" return vector<T,3>(\n")
SLANG_RAW(" left.y * right.z - left.z * right.y,\n")
SLANG_RAW(" left.z * right.x - left.x * right.z,\n")
SLANG_RAW(" left.x * right.y - left.y * right.x);\n")
-SLANG_RAW("}*/\n")
-SLANG_RAW("\n")
+SLANG_RAW("}\n")
SLANG_RAW("\n")
SLANG_RAW("// Convert encoded color\n")
SLANG_RAW("__target_intrinsic(hlsl)\n")
@@ -883,54 +909,83 @@ SLANG_RAW("__target_intrinsic(glsl, dFdx)\n")
SLANG_RAW("T ddx(T x);\n")
SLANG_RAW("\n")
SLANG_RAW("__generic<T : __BuiltinFloatingPointType, let N : int>\n")
+SLANG_RAW("__target_intrinsic(hlsl)\n")
SLANG_RAW("__target_intrinsic(glsl, dFdx)\n")
-SLANG_RAW("vector<T,N> ddx(vector<T,N> x);\n")
+SLANG_RAW("vector<T, N> ddx(vector<T, N> x)\n")
+SLANG_RAW("{\n")
+SLANG_RAW(" VECTOR_MAP_UNARY(T, N, ddx, x);\n")
+SLANG_RAW("}\n")
SLANG_RAW("\n")
SLANG_RAW("__generic<T : __BuiltinFloatingPointType, let N : int, let M : int>\n")
-SLANG_RAW("__target_intrinsic(glsl, dFdx)\n")
-SLANG_RAW("matrix<T,N,M> ddx(matrix<T,N,M> x);\n")
+SLANG_RAW("__target_intrinsic(hlsl)\n")
+SLANG_RAW("matrix<T, N, M> ddx(matrix<T, N, M> x)\n")
+SLANG_RAW("{\n")
+SLANG_RAW(" MATRIX_MAP_UNARY(T, N, M, ddx, x);\n")
+SLANG_RAW("}\n")
SLANG_RAW("\n")
SLANG_RAW("__generic<T : __BuiltinFloatingPointType>\n")
+SLANG_RAW("__target_intrinsic(hlsl)\n")
SLANG_RAW("__glsl_extension(GL_ARB_derivative_control)\n")
SLANG_RAW("__target_intrinsic(glsl, dFdxCoarse)\n")
SLANG_RAW("T ddx_coarse(T x);\n")
SLANG_RAW("\n")
SLANG_RAW("__generic<T : __BuiltinFloatingPointType, let N : int>\n")
+SLANG_RAW("__target_intrinsic(hlsl)\n")
SLANG_RAW("__glsl_extension(GL_ARB_derivative_control)\n")
SLANG_RAW("__target_intrinsic(glsl, dFdxCoarse)\n")
-SLANG_RAW("vector<T,N> ddx_coarse(vector<T,N> x);\n")
+SLANG_RAW("vector<T, N> ddx_coarse(vector<T, N> x)\n")
+SLANG_RAW("{\n")
+SLANG_RAW(" VECTOR_MAP_UNARY(T, N, ddx_coarse, x);\n")
+SLANG_RAW("}\n")
SLANG_RAW("\n")
SLANG_RAW("__generic<T : __BuiltinFloatingPointType, let N : int, let M : int>\n")
-SLANG_RAW("__glsl_extension(GL_ARB_derivative_control)\n")
-SLANG_RAW("__target_intrinsic(glsl, dFdxCoarse)\n")
-SLANG_RAW("matrix<T,N,M> ddx_coarse(matrix<T,N,M> x);\n")
+SLANG_RAW("__target_intrinsic(hlsl)\n")
+SLANG_RAW("matrix<T, N, M> ddx_coarse(matrix<T, N, M> x)\n")
+SLANG_RAW("{\n")
+SLANG_RAW(" MATRIX_MAP_UNARY(T, N, M, ddx_coarse, x);\n")
+SLANG_RAW("}\n")
SLANG_RAW("\n")
SLANG_RAW("__generic<T : __BuiltinFloatingPointType>\n")
+SLANG_RAW("__target_intrinsic(hlsl)\n")
SLANG_RAW("__glsl_extension(GL_ARB_derivative_control)\n")
SLANG_RAW("__target_intrinsic(glsl, dFdxFine)\n")
SLANG_RAW("T ddx_fine(T x);\n")
SLANG_RAW("\n")
SLANG_RAW("__generic<T : __BuiltinFloatingPointType, let N : int>\n")
+SLANG_RAW("__target_intrinsic(hlsl)\n")
SLANG_RAW("__glsl_extension(GL_ARB_derivative_control)\n")
SLANG_RAW("__target_intrinsic(glsl, dFdxFine)\n")
-SLANG_RAW("vector<T,N> ddx_fine(vector<T,N> x);\n")
+SLANG_RAW("vector<T, N> ddx_fine(vector<T, N> x)\n")
+SLANG_RAW("{\n")
+SLANG_RAW(" VECTOR_MAP_UNARY(T, N, ddx_fine, x);\n")
+SLANG_RAW("}\n")
SLANG_RAW("\n")
SLANG_RAW("__generic<T : __BuiltinFloatingPointType, let N : int, let M : int>\n")
-SLANG_RAW("__glsl_extension(GL_ARB_derivative_control)\n")
-SLANG_RAW("__target_intrinsic(glsl, dFdxFine)\n")
-SLANG_RAW("matrix<T,N,M> ddx_fine(matrix<T,N,M> x);\n")
+SLANG_RAW("__target_intrinsic(hlsl)\n")
+SLANG_RAW("matrix<T, N, M> ddx_fine(matrix<T, N, M> x)\n")
+SLANG_RAW("{\n")
+SLANG_RAW(" MATRIX_MAP_UNARY(T, N, M, ddx_fine, x);\n")
+SLANG_RAW("}\n")
SLANG_RAW("\n")
SLANG_RAW("__generic<T : __BuiltinFloatingPointType>\n")
+SLANG_RAW("__target_intrinsic(hlsl)\n")
SLANG_RAW("__target_intrinsic(glsl, dFdy)\n")
SLANG_RAW("T ddy(T x);\n")
SLANG_RAW("\n")
SLANG_RAW("__generic<T : __BuiltinFloatingPointType, let N : int>\n")
+SLANG_RAW("__target_intrinsic(hlsl)\n")
SLANG_RAW("__target_intrinsic(glsl, dFdy)\n")
-SLANG_RAW("vector<T,N> ddy(vector<T,N> x);\n")
+SLANG_RAW("vector<T, N> ddy(vector<T, N> x)\n")
+SLANG_RAW("{\n")
+SLANG_RAW(" VECTOR_MAP_UNARY(T, N, ddy, x);\n")
+SLANG_RAW("}\n")
SLANG_RAW("\n")
SLANG_RAW("__generic<T : __BuiltinFloatingPointType, let N : int, let M : int>\n")
-SLANG_RAW("__target_intrinsic(glsl, dFdy)\n")
-SLANG_RAW(" matrix<T,N,M> ddy(matrix<T,N,M> x);\n")
+SLANG_RAW("__target_intrinsic(hlsl)\n")
+SLANG_RAW("matrix<T, N, M> ddy(matrix<T, N, M> x)\n")
+SLANG_RAW("{\n")
+SLANG_RAW(" MATRIX_MAP_UNARY(T, N, M, ddy, x);\n")
+SLANG_RAW("}\n")
SLANG_RAW("\n")
SLANG_RAW("__generic<T : __BuiltinFloatingPointType>\n")
SLANG_RAW("__glsl_extension(GL_ARB_derivative_control)\n")
@@ -938,44 +993,61 @@ SLANG_RAW("__target_intrinsic(glsl, dFdyCoarse)\n")
SLANG_RAW("T ddy_coarse(T x);\n")
SLANG_RAW("\n")
SLANG_RAW("__generic<T : __BuiltinFloatingPointType, let N : int>\n")
+SLANG_RAW("__target_intrinsic(hlsl)\n")
SLANG_RAW("__glsl_extension(GL_ARB_derivative_control)\n")
SLANG_RAW("__target_intrinsic(glsl, dFdyCoarse)\n")
-SLANG_RAW("vector<T,N> ddy_coarse(vector<T,N> x);\n")
+SLANG_RAW("vector<T, N> ddy_coarse(vector<T, N> x)\n")
+SLANG_RAW("{\n")
+SLANG_RAW(" VECTOR_MAP_UNARY(T, N, ddy_coarse, x);\n")
+SLANG_RAW("}\n")
SLANG_RAW("\n")
SLANG_RAW("__generic<T : __BuiltinFloatingPointType, let N : int, let M : int>\n")
-SLANG_RAW("__glsl_extension(GL_ARB_derivative_control)\n")
-SLANG_RAW("__target_intrinsic(glsl, dFdyCoarse)\n")
-SLANG_RAW("matrix<T,N,M> ddy_coarse(matrix<T,N,M> x);\n")
+SLANG_RAW("__target_intrinsic(hlsl)\n")
+SLANG_RAW("matrix<T, N, M> ddy_coarse(matrix<T, N, M> x)\n")
+SLANG_RAW("{\n")
+SLANG_RAW(" MATRIX_MAP_UNARY(T, N, M, ddy_coarse, x);\n")
+SLANG_RAW("}\n")
SLANG_RAW("\n")
SLANG_RAW("__generic<T : __BuiltinFloatingPointType>\n")
+SLANG_RAW("__target_intrinsic(hlsl)\n")
SLANG_RAW("__glsl_extension(GL_ARB_derivative_control)\n")
SLANG_RAW("__target_intrinsic(glsl, dFdyFine)\n")
SLANG_RAW("T ddy_fine(T x);\n")
SLANG_RAW("\n")
SLANG_RAW("__generic<T : __BuiltinFloatingPointType, let N : int>\n")
+SLANG_RAW("__target_intrinsic(hlsl)\n")
SLANG_RAW("__glsl_extension(GL_ARB_derivative_control)\n")
SLANG_RAW("__target_intrinsic(glsl, dFdyFine)\n")
-SLANG_RAW("vector<T,N> ddy_fine(vector<T,N> x);\n")
+SLANG_RAW("vector<T, N> ddy_fine(vector<T, N> x)\n")
+SLANG_RAW("{\n")
+SLANG_RAW(" VECTOR_MAP_UNARY(T, N, ddy_fine, x);\n")
+SLANG_RAW("}\n")
SLANG_RAW("\n")
SLANG_RAW("__generic<T : __BuiltinFloatingPointType, let N : int, let M : int>\n")
-SLANG_RAW("__glsl_extension(GL_ARB_derivative_control)\n")
-SLANG_RAW("__target_intrinsic(glsl, dFdyFine)\n")
-SLANG_RAW("matrix<T,N,M> ddy_fine(matrix<T,N,M> x);\n")
+SLANG_RAW("__target_intrinsic(hlsl)\n")
+SLANG_RAW("matrix<T, N, M> ddy_fine(matrix<T, N, M> x)\n")
+SLANG_RAW("{\n")
+SLANG_RAW(" MATRIX_MAP_UNARY(T, N, M, ddy_fine, x);\n")
+SLANG_RAW("}\n")
SLANG_RAW("\n")
SLANG_RAW("\n")
SLANG_RAW("// Radians to degrees\n")
SLANG_RAW("\n")
SLANG_RAW("__generic<T : __BuiltinFloatingPointType>\n")
SLANG_RAW("__target_intrinsic(hlsl)\n")
-SLANG_RAW("__target_intrinsic(hlsl)\n")
-SLANG_RAW("T degrees(T x);\n")
-SLANG_RAW("// TODO: filling this in here requires ability to invoke `operator*` on T,\n")
-SLANG_RAW("// and convert a constant to `T` for the conversion factor\n")
+SLANG_RAW("__target_intrinsic(glsl)\n")
+SLANG_RAW("T degrees(T x)\n")
+SLANG_RAW("{\n")
+SLANG_RAW(" return x * (T(180) / T.getPi());\n")
+SLANG_RAW("}\n")
SLANG_RAW("\n")
SLANG_RAW("__generic<T : __BuiltinFloatingPointType, let N : int>\n")
SLANG_RAW("__target_intrinsic(hlsl)\n")
-SLANG_RAW("__target_intrinsic(hlsl)\n")
-SLANG_RAW("vector<T,N> degrees(vector<T,N> x);\n")
+SLANG_RAW("__target_intrinsic(glsl)\n")
+SLANG_RAW("vector<T, N> degrees(vector<T, N> x)\n")
+SLANG_RAW("{\n")
+SLANG_RAW(" VECTOR_MAP_UNARY(T, N, degrees, x);\n")
+SLANG_RAW("}\n")
SLANG_RAW("\n")
SLANG_RAW("__generic<T : __BuiltinFloatingPointType, let N : int, let M : int>\n")
SLANG_RAW("__target_intrinsic(hlsl)\n")
@@ -999,11 +1071,26 @@ SLANG_RAW("void DeviceMemoryBarrierWithGroupSync();\n")
SLANG_RAW("\n")
SLANG_RAW("// Vector distance\n")
SLANG_RAW("\n")
-SLANG_RAW("__generic<T : __BuiltinFloatingPointType, let N : int> T distance(vector<T,N> x, vector<T,N> y);\n")
+SLANG_RAW("__generic<T : __BuiltinFloatingPointType, let N : int>\n")
+SLANG_RAW("__target_intrinsic(hlsl)\n")
+SLANG_RAW("__target_intrinsic(glsl)\n")
+SLANG_RAW("T distance(vector<T, N> x, vector<T, N> y)\n")
+SLANG_RAW("{\n")
+SLANG_RAW(" return length(x - y);\n")
+SLANG_RAW("}\n")
SLANG_RAW("\n")
SLANG_RAW("// Vector dot product\n")
SLANG_RAW("\n")
-SLANG_RAW("__generic<T : __BuiltinArithmeticType, let N : int> T dot(vector<T,N> x, vector<T,N> y);\n")
+SLANG_RAW("__generic<T : __BuiltinArithmeticType, let N : int>\n")
+SLANG_RAW("__target_intrinsic(hlsl)\n")
+SLANG_RAW("__target_intrinsic(glsl)\n")
+SLANG_RAW("T dot(vector<T, N> x, vector<T, N> y)\n")
+SLANG_RAW("{\n")
+SLANG_RAW(" T result = T(0);\n")
+SLANG_RAW(" for(int i = 0; i < N; ++i)\n")
+SLANG_RAW(" result += x[i] * y[i];\n")
+SLANG_RAW(" return result;\n")
+SLANG_RAW("}\n")
SLANG_RAW("\n")
SLANG_RAW("// Helper for computing distance terms for lighting (obsolete)\n")
SLANG_RAW("\n")
@@ -1116,10 +1203,10 @@ SLANG_RAW("// Flip surface normal to face forward, if needed\n")
SLANG_RAW("__generic<T : __BuiltinFloatingPointType, let N : int>\n")
SLANG_RAW("__target_intrinsic(hlsl)\n")
SLANG_RAW("__target_intrinsic(glsl)\n")
-SLANG_RAW("vector<T,N> faceforward(vector<T,N> n, vector<T,N> i, vector<T,N> ng);\n")
-SLANG_RAW("/*{\n")
+SLANG_RAW("vector<T,N> faceforward(vector<T,N> n, vector<T,N> i, vector<T,N> ng)\n")
+SLANG_RAW("{\n")
SLANG_RAW(" return dot(ng, i) < T(0.0f) ? n : -n;\n")
-SLANG_RAW("}*/\n")
+SLANG_RAW("}\n")
SLANG_RAW("\n")
SLANG_RAW("// Find first set bit starting at high bit and working down\n")
SLANG_RAW("__target_intrinsic(glsl,\"findMSB\")\n")
@@ -2460,7 +2547,7 @@ for (int aa = 0; aa < kBaseBufferAccessLevelCount; ++aa)
sb << "};\n";
}
-SLANG_RAW("#line 2387 \"hlsl.meta.slang\"")
+SLANG_RAW("#line 2474 \"hlsl.meta.slang\"")
SLANG_RAW("\n")
SLANG_RAW("\n")
SLANG_RAW("\n")
diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp
index c2b356eff..ad3f506a6 100644
--- a/source/slang/slang-check-decl.cpp
+++ b/source/slang/slang-check-decl.cpp
@@ -1407,7 +1407,7 @@ namespace Slang
bool SemanticsVisitor::findWitnessForInterfaceRequirement(
ConformanceCheckingContext* context,
- DeclRef<AggTypeDeclBase> typeDeclRef,
+ Type* type,
InheritanceDecl* inheritanceDecl,
DeclRef<InterfaceDecl> interfaceDeclRef,
DeclRef<Decl> requiredMemberDeclRef,
@@ -1447,7 +1447,7 @@ namespace Slang
RefPtr<WitnessTable> satisfyingWitnessTable = checkConformanceToType(
context,
- typeDeclRef,
+ type,
requiredInheritanceDeclRef.getDecl(),
getBaseType(requiredInheritanceDeclRef));
@@ -1464,42 +1464,34 @@ namespace Slang
// since only same-name members will be able to
// satisfy the requirement.
//
- // TODO: this won't work right now for members that
- // don't have names, which right now includes
- // initializers/constructors.
Name* name = requiredMemberDeclRef.GetName();
- // We are basically looking up members of the
- // given type, but we need to be a bit careful.
- // We *cannot* perfom lookup "through" inheritance
- // declarations for this or other interfaces,
- // since that would let us satisfy a requirement
- // with itself.
- //
- // There's also an interesting question of whether
- // we can/should support innterface requirements
- // being satisfied via `__transparent` members.
- // This seems like a "clever" idea rather than
- // a useful one, and IR generation would
- // need to construct real IR to trampoline over
- // to the implementation.
- //
- // The final case that can't be reduced to just
- // "a directly declared member with the same name"
- // is the case where the type inherits a member
- // that can satisfy the requirement from a base type.
- // We are ignoring implementation inheritance for
- // now, so we won't worry about this.
-
- // Make sure that by-name lookup is possible.
- buildMemberDictionary(typeDeclRef.getDecl());
- auto lookupResult = lookUpLocal(getSession(), this, name, typeDeclRef);
-
- if (!lookupResult.isValid())
- {
- getSink()->diagnose(inheritanceDecl, Diagnostics::typeDoesntImplementInterfaceRequirement, typeDeclRef, requiredMemberDeclRef);
- return false;
- }
+ // We start by looking up members of the same
+ // name, on the type that is claiming to conform.
+ //
+ // This lookup step could include members that
+ // we might not actually want to consider:
+ //
+ // * Lookup through a type `Foo` where `Foo : IBar`
+ // will be able to find members of `IBar`, which
+ // somewhat obviously shouldn't apply when
+ // determining if `Foo` satisfies the requirements
+ // of `IBar`.
+ //
+ // * Lookup in the presence of `__transparent` members
+ // may produce references to declarations on a *field*
+ // of the type rather than the type. Conformance through
+ // transparent members could be supported in theory,
+ // but would require synthesizing proxy/forwarding
+ // implementations in the type itself.
+ //
+ // We will punt on the second issue for now (since
+ // transparent members aren't currently exposed as
+ // a general-purpose feature for users), and rely
+ // on subsequent checking in this function to
+ // rule out inherited abstract members.
+ //
+ auto lookupResult = lookUpMember(getSession(), this, name, type);
// Iterate over the members and look for one that matches
// the expected signature for the requirement.
@@ -1516,13 +1508,13 @@ namespace Slang
// of "candidates" for satisfaction of the requirement,
// and if nothing is found we print the candidates
- getSink()->diagnose(inheritanceDecl, Diagnostics::typeDoesntImplementInterfaceRequirement, typeDeclRef, requiredMemberDeclRef);
+ getSink()->diagnose(inheritanceDecl, Diagnostics::typeDoesntImplementInterfaceRequirement, type, requiredMemberDeclRef);
return false;
}
RefPtr<WitnessTable> SemanticsVisitor::checkInterfaceConformance(
ConformanceCheckingContext* context,
- DeclRef<AggTypeDeclBase> typeDeclRef,
+ Type* type,
InheritanceDecl* inheritanceDecl,
DeclRef<InterfaceDecl> interfaceDeclRef)
{
@@ -1564,7 +1556,7 @@ namespace Slang
{
auto requirementSatisfied = findWitnessForInterfaceRequirement(
context,
- typeDeclRef,
+ type,
inheritanceDecl,
interfaceDeclRef,
requiredMemberDeclRef,
@@ -1615,7 +1607,7 @@ namespace Slang
{
auto requirementSatisfied = findWitnessForInterfaceRequirement(
context,
- typeDeclRef,
+ type,
inheritanceDecl,
interfaceDeclRef,
requiredInheritanceDeclRef,
@@ -1638,7 +1630,7 @@ namespace Slang
RefPtr<WitnessTable> SemanticsVisitor::checkConformanceToType(
ConformanceCheckingContext* context,
- DeclRef<AggTypeDeclBase> typeDeclRef,
+ Type* type,
InheritanceDecl* inheritanceDecl,
Type* baseType)
{
@@ -1652,7 +1644,7 @@ namespace Slang
// required by that interface.
return checkInterfaceConformance(
context,
- typeDeclRef,
+ type,
inheritanceDecl,
baseInterfaceDeclRef);
}
@@ -1663,41 +1655,43 @@ namespace Slang
}
bool SemanticsVisitor::checkConformance(
- DeclRef<AggTypeDeclBase> declRef,
+ Type* type,
InheritanceDecl* inheritanceDecl)
{
- declRef = createDefaultSubstitutionsIfNeeded(getSession(), declRef).as<AggTypeDeclBase>();
-
- // Don't check conformances for abstract types that
- // are being used to express *required* conformances.
- if (auto assocTypeDeclRef = declRef.as<AssocTypeDecl>())
- {
- // An associated type declaration represents a requirement
- // in an outer interface declaration, and its members
- // (type constraints) represent additional requirements.
- return true;
- }
- else if (auto interfaceDeclRef = declRef.as<InterfaceDecl>())
+ if( auto declRefType = as<DeclRefType>(type) )
{
- // HACK: Our semantics as they stand today are that an
- // `extension` of an interface that adds a new inheritance
- // clause acts *as if* that inheritnace clause had been
- // attached to the original `interface` decl: that is,
- // it adds additional requirements.
- //
- // This is *not* a reasonable semantic to keep long-term,
- // but it is required for some of our current example
- // code to work.
- return true;
- }
+ auto declRef = declRefType->declRef;
+ // Don't check conformances for abstract types that
+ // are being used to express *required* conformances.
+ if (auto assocTypeDeclRef = declRef.as<AssocTypeDecl>())
+ {
+ // An associated type declaration represents a requirement
+ // in an outer interface declaration, and its members
+ // (type constraints) represent additional requirements.
+ return true;
+ }
+ else if (auto interfaceDeclRef = declRef.as<InterfaceDecl>())
+ {
+ // HACK: Our semantics as they stand today are that an
+ // `extension` of an interface that adds a new inheritance
+ // clause acts *as if* that inheritnace clause had been
+ // attached to the original `interface` decl: that is,
+ // it adds additional requirements.
+ //
+ // This is *not* a reasonable semantic to keep long-term,
+ // but it is required for some of our current example
+ // code to work.
+ return true;
+ }
+ }
// Look at the type being inherited from, and validate
// appropriately.
auto baseType = inheritanceDecl->base.type;
ConformanceCheckingContext context;
- RefPtr<WitnessTable> witnessTable = checkConformanceToType(&context, declRef, inheritanceDecl, baseType);
+ RefPtr<WitnessTable> witnessTable = checkConformanceToType(&context, type, inheritanceDecl, baseType);
if(!witnessTable)
return false;
@@ -1707,15 +1701,12 @@ namespace Slang
void SemanticsVisitor::checkExtensionConformance(ExtensionDecl* decl)
{
- if (auto targetDeclRefType = as<DeclRefType>(decl->targetType))
+ auto declRef = createDefaultSubstitutionsIfNeeded(getSession(), makeDeclRef(decl)).as<ExtensionDecl>();
+ auto targetType = GetTargetType(declRef);
+
+ for (auto inheritanceDecl : decl->getMembersOfType<InheritanceDecl>())
{
- if (auto aggTypeDeclRef = targetDeclRefType->declRef.as<AggTypeDecl>())
- {
- for (auto inheritanceDecl : decl->getMembersOfType<InheritanceDecl>())
- {
- checkConformance(aggTypeDeclRef, inheritanceDecl);
- }
- }
+ checkConformance(targetType, inheritanceDecl);
}
}
@@ -1740,6 +1731,10 @@ namespace Slang
{
// For non-interface types we need to check conformance.
//
+
+ auto declRef = createDefaultSubstitutionsIfNeeded(getSession(), makeDeclRef(decl)).as<AggTypeDeclBase>();
+ auto type = DeclRefType::Create(getSession(), declRef);
+
// TODO: Need to figure out what this should do for
// `abstract` types if we ever add them. Should they
// be required to implement all interface requirements,
@@ -1747,7 +1742,7 @@ namespace Slang
// (That's what C# does).
for (auto inheritanceDecl : decl->getMembersOfType<InheritanceDecl>())
{
- checkConformance(makeDeclRef(decl), inheritanceDecl);
+ checkConformance(type, inheritanceDecl);
}
}
}
@@ -2070,7 +2065,7 @@ namespace Slang
void SemanticsVisitor::getGenericParams(
GenericDecl* decl,
List<Decl*>& outParams,
- List<GenericTypeConstraintDecl*> outConstraints)
+ List<GenericTypeConstraintDecl*>& outConstraints)
{
for (auto dd : decl->Members)
{
@@ -2087,100 +2082,223 @@ namespace Slang
}
bool SemanticsVisitor::doGenericSignaturesMatch(
- GenericDecl* fst,
- GenericDecl* snd)
- {
- // First we'll extract the parameters and constraints
- // in each generic signature. We will consider parameters
- // and constraints separately so that we are independent
- // of the order in which constraints are given (that is,
- // a constraint like `<T : IFoo>` would be considered
- // the same as `<T>` with a later `where T : IFoo`.
-
- List<Decl*> fstParams;
- List<GenericTypeConstraintDecl*> fstConstraints;
- getGenericParams(fst, fstParams, fstConstraints);
-
- List<Decl*> sndParams;
- List<GenericTypeConstraintDecl*> sndConstraints;
- getGenericParams(snd, sndParams, sndConstraints);
-
- // For there to be any hope of a match, the
- // two need to have the same number of parameters.
- Index paramCount = fstParams.getCount();
- if (paramCount != sndParams.getCount())
+ GenericDecl* left,
+ GenericDecl* right,
+ RefPtr<GenericSubstitution>* outSubstRightToLeft)
+ {
+ // Our first goal here is to determine if `left` and
+ // `right` have equivalent lists of explicit
+ // generic parameters.
+ //
+ // Once we have determined that the explicit generic
+ // parameters match, we will look at the constraints
+ // placed on those parameters to see if they are
+ // equivalent.
+ //
+ // We thus start by extracting the explicit parameters
+ // and the constraints from each declaration.
+ //
+ List<Decl*> leftParams;
+ List<GenericTypeConstraintDecl*> leftConstraints;
+ getGenericParams(left, leftParams, leftConstraints);
+
+ List<Decl*> rightParams;
+ List<GenericTypeConstraintDecl*> rightConstraints;
+ getGenericParams(right, rightParams, rightConstraints);
+
+ // For there to be any hope of a match, the two decls
+ // need to have the same number of explicit parameters.
+ //
+ Index paramCount = leftParams.getCount();
+ if(paramCount != rightParams.getCount())
return false;
- // Now we'll walk through the parameters.
- for (Index pp = 0; pp < paramCount; ++pp)
+ // Next we will walk through the parameters and look
+ // for a pair-wise match.
+ //
+ for(Index pp = 0; pp < paramCount; ++pp)
{
- Decl* fstParam = fstParams[pp];
- Decl* sndParam = sndParams[pp];
+ Decl* leftParam = leftParams[pp];
+ Decl* rightParam = rightParams[pp];
- if (auto fstTypeParam = as<GenericTypeParamDecl>(fstParam))
+ if (auto leftTypeParam = as<GenericTypeParamDecl>(leftParam))
{
- if (auto sndTypeParam = as<GenericTypeParamDecl>(sndParam))
+ if (auto rightTypeParam = as<GenericTypeParamDecl>(rightParam))
{
- // TODO: is there any validation that needs to be performed here?
- }
- else
- {
- // Type and non-type parameters can't match.
- return false;
+ // Right now any two type parameters are a match.
+ // Names are irrelevant to matching, and any constraints
+ // on the type parameters are represented as implicit
+ // extra parameters of the generic.
+ //
+ // TODO: If we ever supported type parameters with
+ // higher kinds we might need to make a check here
+ // that the kind of each parameter matches (which
+ // would in a sense be a kind of recursive check
+ // of the generic signature of the parameter).
+ //
+ continue;
}
}
- else if (auto fstValueParam = as<GenericValueParamDecl>(fstParam))
+ else if (auto leftValueParam = as<GenericValueParamDecl>(leftParam))
{
- if (auto sndValueParam = as<GenericValueParamDecl>(sndParam))
+ if (auto rightValueParam = as<GenericValueParamDecl>(rightParam))
{
- // Need to check that the parameters have the same type.
+ // In this case we have two generic value parameters,
+ // and they should only be considered to match if
+ // they have the same type.
//
// Note: We are assuming here that the type of a value
// parameter cannot be dependent on any of the type
// parameters in the same signature. This is a reasonable
// assumption for now, but could get thorny down the road.
- if (!fstValueParam->getType()->Equals(sndValueParam->getType()))
+ //
+ if (!leftValueParam->getType()->Equals(rightValueParam->getType()))
{
- // Type mismatch.
+ // If the value parameters have non-matching types,
+ // then the full generic signatures do not match.
+ //
return false;
}
- // TODO: This is not the right place to check on default
- // values for the parameter, because they won't affect
- // the signature, but we should make sure to do validation
- // later on (e.g., that only one declaration can/should
- // be allowed to provide a default).
- }
- else
- {
- // Value and non-value parameters can't match.
- return false;
+ // Generic value parameters with the same type are
+ // always considered to match.
+ //
+ continue;
}
}
+
+ // If we get to this point, then we have two parameters that
+ // were of different syntatic categories (e.g., one type parameter
+ // and one value parameter), so the signatures clearly don't match.
+ //
+ return false;
}
- // If we got this far, then it means the parameter signatures *seem*
- // to match up all right, but now we need to check that the constraints
- // placed on those parameters are also consistent.
- //
- // For now I'm going to assume/require that all declarations must
- // declare the signature in a way that matches exactly.
- Index constraintCount = fstConstraints.getCount();
- if(constraintCount != sndConstraints.getCount())
+ // At this point we know that the explicit generic parameters
+ // of `left` and `right` are aligned, but we need to check
+ // that the constraints that each declaration places on
+ // its parameters match.
+ //
+ // A first challenge that arises is that `left` and `right`
+ // will each express the constraints in terms of their
+ // own parameters. For example, consider the following
+ // declarations:
+ //
+ // void foo1<T : IFoo>(T value);
+ // void foo2<U : IFoo>(U value);
+ //
+ // It is "obvious" to a human that the signatures here
+ // match, but `foo1` has a constraint `T : IFoo` while
+ // `foo2` has a constraint `U : IFoo`, and since `T`
+ // and `U` are distinct `Decl`s, those constraints
+ // are not obviously equivalent.
+ //
+ // We will work around this first issue by creating
+ // a substitution taht lists all the parameters of
+ // `left`, which we can use to specialize `right`
+ // so that it aligns.
+ //
+ // In terms of the example above, this is like constructing
+ // `foo2<T>` so that its constraint, after specialization,
+ // looks like `T : IFoo`.
+ //
+ auto& substRightToLeft = *outSubstRightToLeft;
+ substRightToLeft = createDummySubstitutions(left);
+ substRightToLeft->genericDecl = right;
+
+ // We should now be able to enumerate the constraints
+ // on `right` in a way that uses the same type parameters
+ // as `left`, using `rightDeclRef`.
+ //
+ // At this point a second problem arises: if/when we support
+ // more flexibility in how generic parameter constraints are
+ // specified, it will be possible for two declarations to
+ // list the "same" constraints in very different ways.
+ //
+ // For example, if we support a `where` clause for separating
+ // the constraints from the parameters, then the following
+ // two declarations should have equivalent signatures:
+ //
+ // void foo1<T>(T value)
+ // where T : IFoo
+ // { ... }
+ //
+ // void foo2<T : IFoo>(T value)
+ // { ... }
+ //
+ // Similarly, if we allow for general compositions of interfaces
+ // to be used as constraints, then there can be more than one
+ // way to specify the same constraints:
+ //
+ // void foo1<T : IFoo&IBar>(T value);
+ // void foo2<T : IBar&IFoo>(T value);
+ //
+ // Adding support for equality constraints in `where` clauses
+ // also creates opportunities for multiple equivalent expressions:
+ //
+ // void foo1<T,U>(...) where T.A == U.A;
+ // void foo2<T,U>(...) where U.A == T.A;
+ //
+ // A robsut version of the checking logic here should attempt
+ // to *canonicalize* all of the constraints. Canonicalization
+ // should involve putting constraints into a deterministic
+ // order (e.g., for a generic with `<T,U>` all the constraints
+ // on `T` should come before those on `U`), rewriting individual
+ // constraints into a canonical form (e.g., `T : IFoo & IBar`
+ // should turn into two constraints: `T : IFoo` and `T : IBar`),
+ // etc.
+ //
+ // Once the constraints are in a canonical form we should be able
+ // to test them for pairwise equivalent. As a safety measure we
+ // could also try to test whether one set of constraints implies
+ // the other (since implication in both directions should imply
+ // equivalence, in which case our canonicalization had better
+ // have produced the same result).
+ //
+ // For now we are taking a simpler short-cut by assuming
+ // that constraints are already in a canonical form, which
+ // is reasonable for now as the syntax only allows a single
+ // constraint per parameter, specified on the parameter itself.
+ //
+ // Under the assumption of canonical constraints, we can
+ // assume that different numbers of constraints must indicate
+ // a signature mismatch.
+ //
+ Index constraintCount = leftConstraints.getCount();
+ if(constraintCount != rightConstraints.getCount())
return false;
for (Index cc = 0; cc < constraintCount; ++cc)
{
- //auto fstConstraint = fstConstraints[cc];
- //auto sndConstraint = sndConstraints[cc];
+ // Note that we use a plain `Decl` pointer for the left
+ // constraint, but need to use a `DeclRef` for the right
+ // constraint so that we can take the substitution
+ // arguments into account.
+ //
+ GenericTypeConstraintDecl* leftConstraint = leftConstraints[cc];
+ DeclRef<GenericTypeConstraintDecl> rightConstraint(rightConstraints[cc], substRightToLeft);
+
+ // For now, every constraint has the form `sub : sup`
+ // to indicate that `sub` must be a subtype of `sup`.
+ //
+ // Two such constraints are equivalent if their `sub`
+ // and `sup` types are pairwise equivalent.
+ //
+ auto leftSub = leftConstraint->sub;
+ auto rightSub = GetSub(rightConstraint);
+ if(!leftSub->Equals(rightSub))
+ return false;
- // TODO: the challenge here is that the
- // constraints are going to be expressed
- // in terms of the parameters, which means
- // we need to be doing substitution here.
+ auto leftSup = leftConstraint->sup;
+ auto rightSup = GetSup(rightConstraint);
+ if(!leftSup->Equals(rightSup))
+ return false;
}
- // HACK: okay, we'll just assume things match for now.
+ // If we have checked all of the (canonicalized) constraints
+ // and found them to be pairwise equivalent then the two
+ // generic signatures seem to match.
+ //
return true;
}
@@ -2313,25 +2431,18 @@ namespace Slang
// If we are working with generic functions, then we need to
// consider if their generic signatures match.
+ //
if(newGenericDecl)
{
- SLANG_ASSERT(oldGenericDecl); // already checked above
- if(!doGenericSignaturesMatch(newGenericDecl, oldGenericDecl))
- return SLANG_OK;
-
- // Now we need specialize the declaration references
- // consistently, so that we can compare.
+ // If one declaration is generic, the other must be.
+ // (This condition was already checked above)
//
- // First we create a "dummy" set of substitutions that
- // just reference the parameters of the first generic.
- //
- auto subst = createDummySubstitutions(newGenericDecl);
- //
- // Then we use those parameters to specialize the *other*
- // generic.
- //
- subst->genericDecl = oldGenericDecl;
- oldDeclRef.substitutions.substitutions = subst;
+ SLANG_ASSERT(oldGenericDecl);
+
+ // As part of checking if the generic signatures match,
+ // we will produce a substitution that can be used to
+ // reference `oldGenericDecl` with the generic parameters
+ // substituted for those of `newDecl`.
//
// One way to think about it is that if we have these
// declarations (ignore the name differences...):
@@ -2342,7 +2453,14 @@ namespace Slang
// // newDecl:
// void foo2<U>(U x);
//
- // Then we will compare `foo2` against `foo1<U>`.
+ // Then we will compare the parameter types of `foo2`
+ // against the specialization `foo1<U>`.
+ //
+ RefPtr<GenericSubstitution> subst;
+ if(!doGenericSignaturesMatch(newGenericDecl, oldGenericDecl, &subst))
+ return SLANG_OK;
+
+ oldDeclRef.substitutions.substitutions = subst;
}
// If the parameter signatures don't match, then don't worry
diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h
index f51e62699..8b32f8612 100644
--- a/source/slang/slang-check-impl.h
+++ b/source/slang/slang-check-impl.h
@@ -742,7 +742,7 @@ namespace Slang
//
bool findWitnessForInterfaceRequirement(
ConformanceCheckingContext* context,
- DeclRef<AggTypeDeclBase> typeDeclRef,
+ Type* type,
InheritanceDecl* inheritanceDecl,
DeclRef<InterfaceDecl> interfaceDeclRef,
DeclRef<Decl> requiredMemberDeclRef,
@@ -754,22 +754,21 @@ namespace Slang
// members to satisfy all the requirements in the interface.
RefPtr<WitnessTable> checkInterfaceConformance(
ConformanceCheckingContext* context,
- DeclRef<AggTypeDeclBase> typeDeclRef,
+ Type* type,
InheritanceDecl* inheritanceDecl,
DeclRef<InterfaceDecl> interfaceDeclRef);
RefPtr<WitnessTable> checkConformanceToType(
ConformanceCheckingContext* context,
- DeclRef<AggTypeDeclBase> typeDeclRef,
+ Type* type,
InheritanceDecl* inheritanceDecl,
Type* baseType);
- // Check that the type (or extension) declaration `declRef`,
- // which declares that it inherits from another type via
- // `inheritanceDecl` actually does what it needs to
- // for that inheritance to be valid.
+ /// Check that `type` which has declared that it inherits from (and/or implements)
+ /// another type via `inheritanceDecl` actually does what it needs to for that
+ /// inheritance to be valid.
bool checkConformance(
- DeclRef<AggTypeDeclBase> declRef,
+ Type* type,
InheritanceDecl* inheritanceDecl);
void checkExtensionConformance(ExtensionDecl* decl);
@@ -787,11 +786,15 @@ namespace Slang
void getGenericParams(
GenericDecl* decl,
List<Decl*>& outParams,
- List<GenericTypeConstraintDecl*> outConstraints);
+ List<GenericTypeConstraintDecl*>& outConstraints);
+ /// Determine if `left` and `right` have matching generic signatures.
+ /// If they do, then outputs a substitution to `ioSubstRightToLeft` that
+ /// can be used to specialize `right` to the parameters of `left`.
bool doGenericSignaturesMatch(
- GenericDecl* fst,
- GenericDecl* snd);
+ GenericDecl* left,
+ GenericDecl* right,
+ RefPtr<GenericSubstitution>* outSubstRightToLeft);
// Check if two functions have the same signature for the purposes
// of overload resolution.
@@ -1124,11 +1127,21 @@ namespace Slang
OverloadCandidate* left,
OverloadCandidate* right);
+ /// If `declRef` representations a specialization of a generic, returns the number of specialized generic arguments.
+ /// Otherwise, returns zero.
+ ///
+ Int getSpecializedParamCount(DeclRef<Decl> const& declRef);
+
/// Compare items `left` and `right` produced by lookup, to see if one should be favored for overloading.
int CompareLookupResultItems(
LookupResultItem const& left,
LookupResultItem const& right);
+ /// Compare items `left` and `right` being considered as overload candidates, and determine if one should be favored for structural reasons.
+ int compareOverloadCandidateSpecificity(
+ LookupResultItem const& left,
+ LookupResultItem const& right);
+
void AddOverloadCandidateInner(
OverloadResolveContext& context,
OverloadCandidate& candidate);
@@ -1237,6 +1250,8 @@ namespace Slang
void formatDeclParams(StringBuilder& sb, DeclRef<Decl> declRef);
+ void formatDeclResultType(StringBuilder& sb, DeclRef<Decl> const& declRef);
+
void formatDeclSignature(StringBuilder& sb, DeclRef<Decl> declRef);
String getDeclSignatureString(DeclRef<Decl> declRef);
diff --git a/source/slang/slang-check-overload.cpp b/source/slang/slang-check-overload.cpp
index ace495512..41aa94a35 100644
--- a/source/slang/slang-check-overload.cpp
+++ b/source/slang/slang-check-overload.cpp
@@ -497,6 +497,33 @@ namespace Slang
return false;
}
+ /// If `declRef` representations a specialization of a generic, returns the number of specialized generic arguments.
+ /// Otherwise, returns zero.
+ ///
+ Int SemanticsVisitor::getSpecializedParamCount(DeclRef<Decl> const& declRef)
+ {
+ if(!declRef)
+ return 0;
+
+ // A specialization of a generic must point at the
+ // "inner" declaration of a generic. That means that
+ // the parent of the decl ref must be a generic.
+ //
+ auto parentGeneric = declRef.GetParent().as<GenericDecl>();
+ if(!parentGeneric)
+ return 0;
+ //
+ // Furthermore, the declaration we are considering
+ // must be the single "inner" declaration of the
+ // parent generic (and not somthing like a generic
+ // parameter).
+ //
+ if( parentGeneric.getDecl()->inner.Ptr() != declRef.getDecl())
+ return 0;
+
+ return CountParameters(parentGeneric).required;
+ }
+
int SemanticsVisitor::CompareLookupResultItems(
LookupResultItem const& left,
LookupResultItem const& right)
@@ -534,6 +561,87 @@ namespace Slang
return 0;
}
+ int SemanticsVisitor::compareOverloadCandidateSpecificity(
+ LookupResultItem const& left,
+ LookupResultItem const& right)
+ {
+ // HACK: if both items refer to the same declaration,
+ // then arbitrarily pick one.
+ if(left.declRef.Equals(right.declRef))
+ return -1;
+
+ // There is a very general rule that we would like to enforce
+ // in principle:
+ //
+ // Given candidates A and B, if A being applicable to some
+ // arguments implies that B is also applicable, but not vice versa,
+ // then A is a more specific/specialized candidate than B.
+ //
+ // A number of conclusions follow from this general rule.
+ // For example, a non-generic declaration will always be
+ // more specific than a generic declaration that was specialized
+ // to matching types:
+ //
+ // int doThing(int a);
+ // T doThing<T>(T a);
+ //
+ // It is clear that if the non-generic `doThing` is applicable
+ // to an argument `x`, then `doThing<int>` is also applicable to
+ // `x`. However, knowing that the generic `doThing` was applicable
+ // to some `y` doesn't tell us that the non-generic `doThing` can
+ // be called on `y`, because `y` could have some type that can't
+ // convert to `int`.
+ //
+ // Similarly, a generic declaration with a subset of the parameters
+ // of another generic is always more specialized:
+ //
+ // int doThing<T>(vector<T,3> value);
+ // int doThing<T, let N : int>(vector<T,N> value);
+ //
+ // Here we know that both overloads can apply to `float3`, but only
+ // one can apply to `float4`, so the first overload is more
+ // specialized/specific.
+ //
+ // As a final example, a generic which places more constraints
+ // on its generic parameters is more specific, all other things
+ // being equal:
+ //
+ // int doThing<T : IFoo>( T value );
+ // int doThing<T>(T value);
+ //
+ // In this case we know that the first overload is applicable
+ // to a strict subset of the types that the second overload can
+ // apply to.
+ //
+ // The above rules represent the idealized principles we want
+ // to implement, but actually implementing that full check here
+ // could make overload resolution far more expensive.
+ //
+ // For now we are going to do something far simpler and hackier,
+ // which is to say that a candidate with more generic parameters
+ // is always preferred over one with fewer.
+ //
+ // TODO: We could extend this definition to account for constraints
+ // on generic parameters in the count, which would handle the
+ // need to prefer a more-constrained generic when possible.
+ //
+ // TODO: In the long run we should clearly replace this with
+ // the more general "does A being applicable imply B being applicable"
+ // test.
+ //
+ // TODO: The principle stated here doesn't take the actual
+ // arguments or their types into account, and it might be that
+ // in some cases disambiguation of which declaration should be
+ // preferred will depend on knowing the actual arguments.
+ //
+ auto leftSpecCount = getSpecializedParamCount(left.declRef);
+ auto rightSpecCount = getSpecializedParamCount(right.declRef);
+ if(leftSpecCount != rightSpecCount)
+ return int(leftSpecCount - rightSpecCount);
+
+ return 0;
+ }
+
int SemanticsVisitor::CompareOverloadCandidates(
OverloadCandidate* left,
OverloadCandidate* right)
@@ -546,17 +654,66 @@ namespace Slang
// the costs of their type conversion sequences
if(left->status == OverloadCandidate::Status::Applicable)
{
+ // If one candidate incurred less cost related to
+ // implicit conversion of arguments to matching
+ // parameter types, then we should prefer that
+ // candidate.
+ //
+ // TODO: This eventually should be refined into
+ // a test that checks conversion cost per-argument,
+ // and only considers a candidate "better" if it
+ // has lower cost for at least one argument, and
+ // does not have higher cost for any.
+ //
if (left->conversionCostSum != right->conversionCostSum)
return left->conversionCostSum - right->conversionCostSum;
- // If all conversion costs match, then we should consider
- // whether one of the two items/declarations should be
- // preferred based on grounds that have nothing to do
- // with applicability or conversion costs.
+ // If both candidates appear to be equally good when it
+ // comes to the per-argument conversions required,
+ // then we have two other categories of criteria we
+ // can look at to disambiguate things:
+ //
+ // 1. We can look at how the lookup process found `left` and `right`
+ // do decide which is a better match based purely on how "far away"
+ // they are for lookup purposes. A canonincal example here would
+ // be if one declaration shadows or overrides the other.
+ //
+ // 2. We can look at parameter lists of `left` and `right`, their types, etc.
+ // do decide which is a better match based purely on structure.
+ // Canonical examples in this case would be preferring a non-generic
+ // candidate over a generic one, preferring a non-variadic candidate
+ // over a variadic one, and preferring a candidate with fewer
+ // default parameters over one with more.
+ //
+ // Deciding how to order/interleave these two categories of criteria
+ // is an important design decision.
+ //
+ // For example, consider:
+ //
+ // float f(float x);
+ //
+ // struct S
+ // {
+ // int f<T>(T x);
+ //
+ // float g(float y) { return f(y); }
+ // }
+ //
+ // In terms of structural/type matching, the global `f` is a more specialized
+ // candidate at the call site, while in terms of lookup/lexical crieteria
+ // the `S.f` declaration is better.
+ //
+ // For now we are considering lookup/overriding concerns first (so
+ // we would bias in favor of selecting `S.f` in the above example), and then
+ // structural/type concerns, but a more nuanced approach may be
+ // required in the future to better match programmer intuition.
//
auto itemDiff = CompareLookupResultItems(left->item, right->item);
if(itemDiff)
return itemDiff;
+ auto specificityDiff = compareOverloadCandidateSpecificity(left->item, right->item);
+ if(specificityDiff)
+ return specificityDiff;
}
return 0;
@@ -988,6 +1145,22 @@ namespace Slang
sb << val->ToString();
}
+ static void formatDeclName(StringBuilder& sb, Decl* decl)
+ {
+ if(as<ConstructorDecl>(decl))
+ {
+ sb << "init";
+ }
+ else if(as<SubscriptDecl>(decl))
+ {
+ sb << "subscript";
+ }
+ else
+ {
+ sb << getText(decl->getName());
+ }
+ }
+
void SemanticsVisitor::formatDeclPath(StringBuilder& sb, DeclRef<Decl> declRef)
{
// Find the parent declaration
@@ -1008,7 +1181,7 @@ namespace Slang
sb << ".";
}
- sb << getText(declRef.GetName());
+ formatDeclName(sb, declRef.getDecl());
// If the parent declaration is a generic, then we need to print out its
// signature
@@ -1018,10 +1191,29 @@ namespace Slang
SLANG_RELEASE_ASSERT(genSubst);
SLANG_RELEASE_ASSERT(genSubst->genericDecl == parentGenericDeclRef.getDecl());
+ // If the name we printed previously was an operator
+ // that ends with `<`, then immediately printing the
+ // generic arguments inside `<...>` may cause it to
+ // be hard to parse the operator name visually.
+ //
+ // We thus include a space between the declaration name
+ // and its generic arguments in this case.
+ //
+ if(sb.endsWith("<")) sb << " ";
+
sb << "<";
bool first = true;
for(auto arg : genSubst->args)
{
+ // When printing the representation of a sepcialized
+ // generic declaration we don't want to include the
+ // argument values for subtype witnesses since these
+ // do not correspond to parameters of the generic
+ // as the user sees it.
+ //
+ if(as<Witness>(arg))
+ continue;
+
if(!first) sb << ", ";
formatVal(sb, arg);
first = false;
@@ -1085,10 +1277,31 @@ namespace Slang
}
}
+ static void formatDeclKindPrefix(StringBuilder& sb, Decl* decl)
+ {
+ if(as<FuncDecl>(decl))
+ {
+ sb << "func ";
+ }
+ }
+
+ void SemanticsVisitor::formatDeclResultType(StringBuilder& sb, DeclRef<Decl> const& declRef)
+ {
+ if(as<ConstructorDecl>(declRef))
+ {}
+ else if(auto callableDeclRef = declRef.as<CallableDecl>())
+ {
+ sb << " -> ";
+ formatType(sb, GetResultType(callableDeclRef));
+ }
+ }
+
void SemanticsVisitor::formatDeclSignature(StringBuilder& sb, DeclRef<Decl> declRef)
{
+ formatDeclKindPrefix(sb, declRef.getDecl());
formatDeclPath(sb, declRef);
formatDeclParams(sb, declRef);
+ formatDeclResultType(sb, declRef);
}
String SemanticsVisitor::getDeclSignatureString(DeclRef<Decl> declRef)
diff --git a/source/slang/slang-emit-cpp.cpp b/source/slang/slang-emit-cpp.cpp
index 4c2e6032d..7fb04c33b 100644
--- a/source/slang/slang-emit-cpp.cpp
+++ b/source/slang/slang-emit-cpp.cpp
@@ -951,33 +951,6 @@ void CPPSourceEmitter::_emitVecMatMulDefinition(const UnownedStringSlice& funcNa
writer->emit("}\n\n");
}
-void CPPSourceEmitter::_emitCrossDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp)
-{
- _emitSignature(funcName, specOp);
-
- SourceWriter* writer = getSourceWriter();
-
- writer->emit("\n{\n");
- writer->indent();
-
- writer->emit("return ");
- if (m_target == CodeGenTarget::CUDASource)
- {
- m_writer->emit("make_");
- emitType(specOp->returnType);
- writer->emit("( a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x ); \n");
- }
- else
- {
- emitType(specOp->returnType);
- writer->emit("{ a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x }; \n");
- }
-
-
- writer->dedent();
- writer->emit("}\n\n");
-}
-
UnownedStringSlice CPPSourceEmitter::_getAndEmitSpecializedOperationDefinition(HLSLIntrinsic::Op op, IRType*const* argTypes, Int argCount, IRType* retType)
{
HLSLIntrinsic intrinsic;
@@ -1422,10 +1395,6 @@ void CPPSourceEmitter::emitSpecializedOperationDefinition(const HLSLIntrinsic* s
{
return _emitAnyAllDefinition(_getFuncName(specOp), specOp);
}
- case Op::Cross:
- {
- return _emitCrossDefinition(_getFuncName(specOp), specOp);
- }
case Op::Normalize:
{
return _emitNormalizeDefinition(_getFuncName(specOp), specOp);
diff --git a/source/slang/slang-emit-cpp.h b/source/slang/slang-emit-cpp.h
index da8d026ee..7f9046643 100644
--- a/source/slang/slang-emit-cpp.h
+++ b/source/slang/slang-emit-cpp.h
@@ -97,7 +97,6 @@ protected:
// Really we don't want any of these defined like they are here, they should be defined in slang stdlib
void _emitAnyAllDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp);
- void _emitCrossDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp);
void _emitLengthDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp);
void _emitNormalizeDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp);
void _emitReflectDefinition(const UnownedStringSlice& funcName, const HLSLIntrinsic* specOp);
diff --git a/source/slang/slang-hlsl-intrinsic-set.h b/source/slang/slang-hlsl-intrinsic-set.h
index 22e5b29e5..6ab5480b3 100644
--- a/source/slang/slang-hlsl-intrinsic-set.h
+++ b/source/slang/slang-hlsl-intrinsic-set.h
@@ -104,7 +104,6 @@ just constructXXXFromScalar. Would be good if there was a suitable name to encom
x(Max, "max", 2) \
x(Pow, "pow", 2) \
x(FMod, "fmod", 2) \
- x(Cross, "cross", 2) \
x(Reflect, "reflect", 2) \
\
x(SmoothStep, "smoothstep", 3) \
diff --git a/source/slang/slang-lookup.cpp b/source/slang/slang-lookup.cpp
index 49f0bbf0c..02f74579c 100644
--- a/source/slang/slang-lookup.cpp
+++ b/source/slang/slang-lookup.cpp
@@ -25,14 +25,6 @@ struct BreadcrumbInfo
BreadcrumbInfo* prev = nullptr;
};
-void DoLocalLookupImpl(
- Session* session,
- Name* name,
- DeclRef<ContainerDecl> containerDeclRef,
- LookupRequest const& request,
- LookupResult& result,
- BreadcrumbInfo* inBreadcrumbs);
-
//
void buildMemberDictionary(ContainerDecl* decl)
@@ -169,146 +161,28 @@ LookupResultItem CreateLookupResultItem(
return item;
}
-void DoMemberLookupImpl(
- Session* session,
- Name* name,
- RefPtr<Type> baseType,
- LookupRequest const& request,
- LookupResult& ioResult,
- BreadcrumbInfo* breadcrumbs)
-{
- if (!baseType)
- {
- return;
- }
-
- // If the type was pointer-like, then dereference it
- // automatically here.
- if (auto pointerLikeType = as<PointerLikeType>(baseType))
- {
- // Need to leave a breadcrumb to indicate that we
- // did an implicit dereference here
- BreadcrumbInfo derefBreacrumb;
- derefBreacrumb.kind = LookupResultItem::Breadcrumb::Kind::Deref;
- derefBreacrumb.prev = breadcrumbs;
-
- // Recursively perform lookup on the result of deref
- return DoMemberLookupImpl(
- session,
- name, pointerLikeType->elementType, request, ioResult, &derefBreacrumb);
- }
-
- // Default case: no dereference needed
-
- if (auto baseDeclRefType = as<DeclRefType>(baseType))
- {
- if (auto baseAggTypeDeclRef = baseDeclRefType->declRef.as<AggTypeDecl>())
- {
- DoLocalLookupImpl(
- session,
- name, baseAggTypeDeclRef, request, ioResult, breadcrumbs);
- }
- }
-
- // TODO(tfoley): any other cases to handle here?
-}
-
-void DoMemberLookupImpl(
+static void _lookUpMembersInValue(
Session* session,
Name* name,
- DeclRef<Decl> baseDeclRef,
+ DeclRef<Decl> valueDeclRef,
LookupRequest const& request,
LookupResult& ioResult,
- BreadcrumbInfo* breadcrumbs)
-{
- auto baseType = getTypeForDeclRef(
- session,
- baseDeclRef,
- SourceLoc());
- return DoMemberLookupImpl(
- session,
- name, baseType, request, ioResult, breadcrumbs);
-}
-
-// If we are about to perform lookup through an interface, then
-// we need to specialize the decl-ref to that interface to include
-// a "this type" subtitution. This function applies that substition
-// when it is required, and returns the existing `declRef` otherwise.
-DeclRef<Decl> maybeSpecializeInterfaceDeclRef(
- RefPtr<Type> subType,
- RefPtr<Type> superType,
- DeclRef<Decl> superTypeDeclRef, // The decl-ref we are going to perform lookup in
- DeclRef<TypeConstraintDecl> constraintDeclRef) // The type constraint that told us our type is a subtype
-{
- if (auto superInterfaceDeclRef = superTypeDeclRef.as<InterfaceDecl>())
- {
- // TODO: This case should probably loop back into the semantic
- // checking logic (when available) in order to ensure that
- // appropriate witness values have been registered for (at least)
- // the associated type requirements of the super-type.
-
- // Create a subtype witness value to note the subtype relationship
- // that makes this specialization valid.
- //
- // Note: this is to ensure that we can specialize the subtype witness
- // later (e.g., by replacing a subtype witness that represents a generic
- // constraint parameter with the concrete generic arguments that
- // are used at a particular call site to the generic).
- RefPtr<DeclaredSubtypeWitness> subtypeWitness = new DeclaredSubtypeWitness();
- subtypeWitness->declRef = constraintDeclRef;
- subtypeWitness->sub = subType;
- subtypeWitness->sup = superType;
-
- RefPtr<ThisTypeSubstitution> thisTypeSubst = new ThisTypeSubstitution();
- thisTypeSubst->interfaceDecl = superInterfaceDeclRef.getDecl();
- thisTypeSubst->witness = subtypeWitness;
- thisTypeSubst->outer = superInterfaceDeclRef.substitutions.substitutions;
-
- auto specializedInterfaceDeclRef = DeclRef<Decl>(superInterfaceDeclRef.getDecl(), thisTypeSubst);
- return specializedInterfaceDeclRef;
- }
-
- return superTypeDeclRef;
-}
-
-// Same as the above, but we are specializing a type instead of a decl-ref
-RefPtr<Type> maybeSpecializeInterfaceDeclRef(
- Session* session,
- RefPtr<Type> subType,
- RefPtr<Type> superType, // The type we are going to perform lookup in
- DeclRef<TypeConstraintDecl> constraintDeclRef) // The type constraint that told us our type is a subtype
-{
- if (auto superDeclRefType = as<DeclRefType>(superType))
- {
- if (auto superInterfaceDeclRef = superDeclRefType->declRef.as<InterfaceDecl>())
- {
- auto specializedInterfaceDeclRef = maybeSpecializeInterfaceDeclRef(
- subType,
- superType,
- superInterfaceDeclRef,
- constraintDeclRef);
- auto specializedInterfaceType = DeclRefType::Create(session, specializedInterfaceDeclRef);
- return specializedInterfaceType;
- }
- }
-
- return superType;
-}
-
-
-// Look for members of the given name in the given container for declarations
-void DoLocalLookupImpl(
+ BreadcrumbInfo* breadcrumbs);
+
+ /// Look up direct members (those declared in `containerDeclRef` itself, as well
+ /// as transitively through any direct members that are marked "transparent."
+ ///
+ /// This function does *not* deal with looking up through `extension`s,
+ /// inheritance clauses, etc.
+ ///
+static void _lookUpDirectAndTransparentMembers(
Session* session,
Name* name,
- DeclRef<ContainerDecl> containerDeclRef,
+ DeclRef<ContainerDecl> containerDeclRef,
LookupRequest const& request,
- LookupResult& result,
- BreadcrumbInfo* inBreadcrumbs)
+ LookupResult& result,
+ BreadcrumbInfo* inBreadcrumbs)
{
- if (result.lookedupDecls.Contains(containerDeclRef))
- return;
- result.lookedupDecls.Add(containerDeclRef);
-
ContainerDecl* containerDecl = containerDeclRef.getDecl();
// Ensure that the lookup dictionary in the container is up to date
@@ -334,10 +208,8 @@ void DoLocalLookupImpl(
AddToLookupResult(result, CreateLookupResultItem(DeclRef<Decl>(m, containerDeclRef.substitutions), inBreadcrumbs));
}
-
// TODO(tfoley): should we look up in the transparent decls
// if we already has a hit in the current container?
-
for(auto transparentInfo : containerDecl->transparentMembers)
{
// The reference to the transparent member should use whatever
@@ -352,7 +224,7 @@ void DoLocalLookupImpl(
memberRefBreadcrumb.declRef = transparentMemberDeclRef;
memberRefBreadcrumb.prev = inBreadcrumbs;
- DoMemberLookupImpl(
+ _lookUpMembersInValue(
session,
name,
transparentMemberDeclRef,
@@ -360,97 +232,363 @@ void DoLocalLookupImpl(
result,
&memberRefBreadcrumb);
}
+}
- // Consider lookup via extension
- if( auto aggTypeDeclRef = containerDeclRef.as<AggTypeDecl>() )
+static RefPtr<SubtypeWitness> _makeSubtypeWitness(
+ Type* subType,
+ SubtypeWitness* subToMidWitness,
+ Type* superType,
+ DeclRef<TypeConstraintDecl> midToSuperConstraint)
+{
+ if(subToMidWitness)
+ {
+ RefPtr<TransitiveSubtypeWitness> transitiveWitness = new TransitiveSubtypeWitness();
+ transitiveWitness->subToMid = subToMidWitness;
+ transitiveWitness->midToSup = midToSuperConstraint;
+ transitiveWitness->sub = subType;
+ transitiveWitness->sup = superType;
+ return transitiveWitness;
+ }
+ else
{
- if (request.semantics)
+ RefPtr<DeclaredSubtypeWitness> declaredWitness = new DeclaredSubtypeWitness();
+ declaredWitness->declRef = midToSuperConstraint;
+ declaredWitness->sub = subType;
+ declaredWitness->sup = superType;
+ return declaredWitness;
+ }
+}
+
+// Same as the above, but we are specializing a type instead of a decl-ref
+static RefPtr<Type> _maybeSpecializeSuperType(
+ Session* session,
+ Type* superType,
+ SubtypeWitness* subIsSuperWitness)
+{
+ if (auto superDeclRefType = as<DeclRefType>(superType))
+ {
+ if (auto superInterfaceDeclRef = superDeclRefType->declRef.as<InterfaceDecl>())
{
- ensureDecl(request.semantics, containerDeclRef.getDecl(), DeclCheckState::ReadyForLookup);
+ RefPtr<ThisTypeSubstitution> thisTypeSubst = new ThisTypeSubstitution();
+ thisTypeSubst->interfaceDecl = superInterfaceDeclRef.getDecl();
+ thisTypeSubst->witness = subIsSuperWitness;
+ thisTypeSubst->outer = superInterfaceDeclRef.substitutions.substitutions;
+
+ auto specializedInterfaceDeclRef = DeclRef<Decl>(superInterfaceDeclRef.getDecl(), thisTypeSubst);
+
+ auto specializedInterfaceType = DeclRefType::Create(session, specializedInterfaceDeclRef);
+ return specializedInterfaceType;
}
+ }
+
+ return superType;
+}
+
+static void _lookUpMembersInType(
+ Session* session,
+ Name* name,
+ RefPtr<Type> type,
+ LookupRequest const& request,
+ LookupResult& ioResult,
+ BreadcrumbInfo* breadcrumbs);
+
+static void _lookUpMembersInSuperTypeImpl(
+ Session* session,
+ Name* name,
+ Type* leafType,
+ Type* superType,
+ SubtypeWitness* leafIsSuperWitness,
+ LookupRequest const& request,
+ LookupResult& ioResult,
+ BreadcrumbInfo* inBreadcrumbs);
- RefPtr<Type> type = DeclRefType::Create(
- session,
- aggTypeDeclRef);
- for (auto ext = GetCandidateExtensions(aggTypeDeclRef); ext; ext = ext->nextCandidateExtension)
+static void _lookUpMembersInSuperType(
+ Session* session,
+ Name* name,
+ Type* leafType,
+ SubtypeWitness* leafIsIntermediateWitness,
+ DeclRef<TypeConstraintDecl> intermediateIsSuperConstraint,
+ LookupRequest const& request,
+ LookupResult& ioResult,
+ BreadcrumbInfo* inBreadcrumbs)
+{
+ if( request.semantics )
+ {
+ ensureDecl(request.semantics, intermediateIsSuperConstraint, DeclCheckState::CanUseBaseOfInheritanceDecl);
+ }
+
+ // The super-type in the constraint (e.g., `Foo` in `T : Foo`)
+ // will tell us a type we should use for lookup.
+ //
+ auto superType = GetSup(intermediateIsSuperConstraint);
+ //
+ // We will go ahead and perform lookup using `superType`,
+ // after dealing with some details.
+
+ auto leafIsSuperWitness = _makeSubtypeWitness(
+ leafType,
+ leafIsIntermediateWitness,
+ superType,
+ intermediateIsSuperConstraint);
+
+ // If we are looking up through an interface type, then
+ // we need to be sure that we add an appropriate
+ // "this type" substitution here, since that needs to
+ // be applied to any members we look up.
+ //
+ superType = _maybeSpecializeSuperType(
+ session,
+ superType,
+ leafIsSuperWitness);
+
+ // We need to track the indirection we took in lookup,
+ // so that we can construct an appropriate AST on the other
+ // side that includes the "upcast" from sub-type to super-type.
+ //
+ BreadcrumbInfo breadcrumb;
+ breadcrumb.prev = inBreadcrumbs;
+ breadcrumb.kind = LookupResultItem::Breadcrumb::Kind::Constraint;
+ breadcrumb.declRef = intermediateIsSuperConstraint;
+ breadcrumb.prev = inBreadcrumbs;
+
+ // TODO: Need to consider case where this might recurse infinitely (e.g.,
+ // if an inheritance clause does something like `Bad<T> : Bad<Bad<T>>`.
+ //
+ // TODO: The even simpler thing we need to worry about here is that if
+ // there is ever a "diamond" relationship in the inheritance hierarchy,
+ // we might end up seeing the same interface via different "paths" and
+ // we wouldn't want that to lead to overload-resolution failure.
+ //
+ _lookUpMembersInSuperTypeImpl(session, name, leafType, superType, leafIsSuperWitness, request, ioResult, &breadcrumb);
+}
+
+static void _lookUpMembersInSuperTypeDeclImpl(
+ Session* session,
+ Name* name,
+ Type* leafType,
+ Type* superType,
+ SubtypeWitness* leafIsSuperWitness,
+ DeclRef<Decl> declRef,
+ LookupRequest const& request,
+ LookupResult& ioResult,
+ BreadcrumbInfo* inBreadcrumbs)
+{
+ auto semantics = request.semantics;
+ if( semantics )
+ {
+ ensureDecl(semantics, declRef.getDecl(), DeclCheckState::ReadyForLookup);
+ }
+
+ if (auto genericTypeParamDeclRef = declRef.as<GenericTypeParamDecl>())
+ {
+ // If the type we are doing lookup in is a generic type parameter,
+ // then the members it provides can only be discovered by looking
+ // at the constraints that are placed on that type.
+
+ auto genericDeclRef = genericTypeParamDeclRef.GetParent().as<GenericDecl>();
+ assert(genericDeclRef);
+
+ for(auto constraintDeclRef : getMembersOfType<GenericTypeConstraintDecl>(genericDeclRef))
{
- auto extDeclRef = ApplyExtensionToType(request.semantics, ext, type);
- if (!extDeclRef)
- continue;
+ if( semantics )
+ {
+ ensureDecl(semantics, constraintDeclRef, DeclCheckState::CanUseBaseOfInheritanceDecl);
+ }
- // TODO: eventually we need to insert a breadcrumb here so that
- // the constructed result can somehow indicate that a member
- // was found through an extension.
+ // Does this constraint pertain to the type we are working on?
+ //
+ // We want constraints of the form `T : Foo` where `T` is the
+ // generic parameter in question, and `Foo` is whatever we are
+ // constraining it to.
+ auto subType = GetSub(constraintDeclRef);
+ auto subDeclRefType = as<DeclRefType>(subType);
+ if(!subDeclRefType)
+ continue;
+ if(!subDeclRefType->declRef.Equals(genericTypeParamDeclRef))
+ continue;
- DoLocalLookupImpl(
+ _lookUpMembersInSuperType(
session,
- name, extDeclRef, request, result, inBreadcrumbs);
+ name,
+ leafType,
+ leafIsSuperWitness,
+ constraintDeclRef,
+ request,
+ ioResult,
+ inBreadcrumbs);
}
-
}
- // for interface decls, also lookup in the base interfaces
- if (request.semantics)
+ else if (declRef.as<AssocTypeDecl>() || declRef.as<GlobalGenericParamDecl>())
{
- // TODO:
- // The logic here is a bit gross, because it tries to work in terms of
- // decl-refs instead of types (e.g., it asserts that the target type
- // for an `extension` declaration must be a decl-ref type).
- //
- // This code should be converted to do a type-based lookup
- // through declared bases for *any* aggregate type declaration.
- // I think that logic is present in the type-based lookup path, but
- // it would be needed here for when doing lookup from inside an
- // aggregate declaration.
-
- // if we are looking at an extension, find the target decl that we are extending
- DeclRef<Decl> targetDeclRef = containerDeclRef;
- RefPtr<DeclRefType> targetDeclRefType;
- if (auto extDeclRef = containerDeclRef.as<ExtensionDecl>())
+ for (auto constraintDeclRef : getMembersOfType<TypeConstraintDecl>(declRef.as<ContainerDecl>()))
{
- ensureDecl(request.semantics, extDeclRef.getDecl(), DeclCheckState::CanUseExtensionTargetType);
-
- targetDeclRefType = as<DeclRefType>(extDeclRef.getDecl()->targetType);
- SLANG_ASSERT(targetDeclRefType);
- int diff = 0;
- targetDeclRef = targetDeclRefType->declRef.as<ContainerDecl>().SubstituteImpl(containerDeclRef.substitutions, &diff);
+ _lookUpMembersInSuperType(
+ session,
+ name,
+ leafType,
+ leafIsSuperWitness,
+ constraintDeclRef,
+ request,
+ ioResult,
+ inBreadcrumbs);
}
-
- // When looking up inside a type, we want to also perform lookup via
- // the types it inherits from, in case of them defines the member we are looking for.
+ }
+ else if(auto aggTypeDeclBaseRef = declRef.as<AggTypeDeclBase>())
+ {
+ // In this case we are peforming lookup in the context of an aggregate
+ // type or an `extension`, so the first thing to do is to look for
+ // matching members declared directly in the body of the type/`extension`.
//
- // TODO: Need to be careful that this doesn't allow a type to satisfy an interface
- // requirement using the original declaration of that requirement...
+ _lookUpDirectAndTransparentMembers(session, name, aggTypeDeclBaseRef, request, ioResult, inBreadcrumbs);
+ // There are further lookup steps that we can only perform when a
+ // semantic checking context is available to us. That means that
+ // during parsing, lookup will fail to find members under `name`
+ // if they required following these paths.
+ //
+ if(semantics)
{
- if(!targetDeclRefType)
+ if(auto aggTypeDeclRef = aggTypeDeclBaseRef.as<AggTypeDecl>())
{
- targetDeclRefType = DeclRefType::Create(session, targetDeclRef);
+ // If the declaration we are looking at is a nominal type declaration,
+ // then we want to consider any `extension`s that have been associated
+ // directly with that type.
+ //
+ ensureDecl(request.semantics, aggTypeDeclRef.getDecl(), DeclCheckState::ReadyForLookup);
+ for(auto extDecl = GetCandidateExtensions(aggTypeDeclRef); extDecl; extDecl = extDecl->nextCandidateExtension)
+ {
+ // Note: In this case `extDecl` is an extension that was declared to apply
+ // (conditionally) to `aggTypeDeclRef`, which is the decl-ref part of
+ // `superType`. Thus when looking for a substitution to apply to the
+ // extension, we need to apply it to `superType` and not to `leafType`.
+ //
+ auto extDeclRef = ApplyExtensionToType(request.semantics, extDecl, superType);
+ if (!extDeclRef)
+ continue;
+
+ // TODO: eventually we need to insert a breadcrumb here so that
+ // the constructed result can somehow indicate that a member
+ // was found through an extension.
+ //
+ _lookUpMembersInSuperTypeDeclImpl(
+ session,
+ name,
+ leafType,
+ superType,
+ leafIsSuperWitness,
+ extDeclRef,
+ request,
+ ioResult,
+ inBreadcrumbs);
+ }
}
- auto baseInterfaces = getMembersOfType<InheritanceDecl>(containerDeclRef);
- for (auto inheritanceDeclRef : baseInterfaces)
+ // For both aggregate types and their `extension`s, we want lookup to follow
+ // through the declared inheritance relationships on each declaration.
+ //
+ ensureDecl(semantics, aggTypeDeclBaseRef.getDecl(), DeclCheckState::CanEnumerateBases);
+ for (auto inheritanceDeclRef : getMembersOfType<InheritanceDecl>(aggTypeDeclBaseRef))
{
- ensureDecl(request.semantics, inheritanceDeclRef.getDecl(), DeclCheckState::CanUseBaseOfInheritanceDecl);
+ ensureDecl(semantics, inheritanceDeclRef.getDecl(), DeclCheckState::CanUseBaseOfInheritanceDecl);
+ _lookUpMembersInSuperType(session, name, leafType, leafIsSuperWitness, inheritanceDeclRef, request, ioResult, inBreadcrumbs);
+ }
+ }
+ }
+}
+
+static void _lookUpMembersInSuperTypeImpl(
+ Session* session,
+ Name* name,
+ Type* leafType,
+ Type* superType,
+ SubtypeWitness* leafIsSuperWitness,
+ LookupRequest const& request,
+ LookupResult& ioResult,
+ BreadcrumbInfo* inBreadcrumbs)
+{
+ // If the type was pointer-like, then dereference it
+ // automatically here.
+ if (auto pointerLikeType = as<PointerLikeType>(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;
- auto baseType = inheritanceDeclRef.getDecl()->base.type.dynamicCast<DeclRefType>();
- SLANG_ASSERT(baseType);
- int diff = 0;
- auto baseInterfaceDeclRef = baseType->declRef.SubstituteImpl(containerDeclRef.substitutions, &diff);
+ // Recursively perform lookup on the result of deref
+ _lookUpMembersInType(
+ session,
+ name, pointerLikeType->elementType, request, ioResult, &derefBreacrumb);
+ return;
+ }
- baseInterfaceDeclRef = maybeSpecializeInterfaceDeclRef(
- targetDeclRefType,
- baseType,
- baseInterfaceDeclRef,
- inheritanceDeclRef);
+ // Default case: no dereference needed
- DoLocalLookupImpl(session, name, baseInterfaceDeclRef.as<ContainerDecl>(), request, result, inBreadcrumbs);
- }
- }
+ if(auto declRefType = as<DeclRefType>(superType))
+ {
+ auto declRef = declRefType->declRef;
+
+ _lookUpMembersInSuperTypeDeclImpl(session, name, leafType, superType, leafIsSuperWitness, declRef, request, ioResult, inBreadcrumbs);
+ }
+}
+
+ /// Perform lookup for `name` in the context of `type`.
+ ///
+ /// This operation does the kind of lookup we'd expect if `name`
+ /// was used inside of a member function on `type`, or if the
+ /// user wrote `obj.<name>` for a variable `obj` of the given
+ /// `type`.
+ ///
+ /// Looking up members in `type` includes lookup through any
+ /// constraints or inheritance relationships that expand the
+ /// set of members visible on `type`.
+ ///
+static void _lookUpMembersInType(
+ Session* session,
+ Name* name,
+ RefPtr<Type> type,
+ LookupRequest const& request,
+ LookupResult& ioResult,
+ BreadcrumbInfo* breadcrumbs)
+{
+ if (!type)
+ {
+ return;
}
+
+ _lookUpMembersInSuperTypeImpl(session, name, type, type, nullptr, request, ioResult, breadcrumbs);
+}
+
+ /// Look up members by `name` in the given `valueDeclRef`.
+ ///
+ /// If `valueDeclRef` represents a reference to a variable
+ /// or other named and typed value, then this performs the
+ /// kind of lookup we'd expect for `valueDeclRef.<name>`.
+ ///
+static void _lookUpMembersInValue(
+ Session* session,
+ Name* name,
+ DeclRef<Decl> valueDeclRef,
+ LookupRequest const& request,
+ LookupResult& ioResult,
+ BreadcrumbInfo* breadcrumbs)
+{
+ // Looking up `name` in the context of a value can
+ // be reduced to the problem of looking up `name`
+ // in the *type* of that value.
+ //
+ auto valueType = getTypeForDeclRef(
+ session,
+ valueDeclRef,
+ SourceLoc());
+ return _lookUpMembersInType(
+ session,
+ name, valueType, request, ioResult, breadcrumbs);
}
-void DoLookupImpl(
+static void _lookUpInScopes(
Session* session,
Name* name,
LookupRequest const& request,
@@ -472,52 +610,74 @@ void DoLookupImpl(
if(!containerDecl)
continue;
+ // TODO: If we need default substitutions to be applied to
+ // the `containerDecl`, then it might make sense to have
+ // each `link` in the scope store a decl-ref instead of
+ // just a decl.
+ //
DeclRef<ContainerDecl> containerDeclRef =
DeclRef<Decl>(containerDecl, createDefaultSubstitutions(session, containerDecl)).as<ContainerDecl>();
- BreadcrumbInfo breadcrumb;
- BreadcrumbInfo* breadcrumbs = nullptr;
-
- // Depending on the kind of container we are looking into,
- // we may need to insert something like a `this` expression
- // to resolve the lookup result.
+ // If the container we are looking into represents a type
+ // or an `extension` of a type, then we need to treat
+ // this step as lookup into the `this` variable (or the
+ // `This` type), which means including any `extension`s
+ // or inheritance clauses in the lookup process.
//
- // Note: We are checking for `AggTypeDeclBase` here, and not
- // just `AggTypeDecl`, because we want to catch `extension`
- // declarations as well.
+ // Note: The `AggTypeDeclBase` class is the common superclass
+ // between `AggTypeDecl` and `ExtensionDecl`.
//
- if (auto aggTypeDeclRef = containerDeclRef.as<AggTypeDeclBase>())
+ if (auto aggTypeDeclBaseRef = containerDeclRef.as<AggTypeDeclBase>())
{
+ // When reconstructing the final expression for a result
+ // looked up through the tyep or extension, we will need
+ // a `this` expression (or a `This` type expression) to
+ // mark the base of the member reference, so we create
+ // a "breadcrumb" here to trakc that fact.
+ //
+ BreadcrumbInfo breadcrumb;
breadcrumb.kind = LookupResultItem::Breadcrumb::Kind::This;
breadcrumb.thisParameterMode = thisParameterMode;
- breadcrumb.declRef = aggTypeDeclRef;
+ breadcrumb.declRef = aggTypeDeclBaseRef;
breadcrumb.prev = nullptr;
- breadcrumbs = &breadcrumb;
- }
-
- // Now perform "local" lookup in the context of the container,
- // as if we were looking up a member directly.
-
- // if we are currently in an extension decl, perform local lookup
- // in the target decl we are extending
- if (auto extDeclRef = containerDeclRef.as<ExtensionDecl>())
- {
- if (extDeclRef.getDecl()->targetType)
+ RefPtr<Type> type;
+ if(auto extDeclRef = aggTypeDeclBaseRef.as<ExtensionDecl>())
{
- if (auto targetDeclRef = as<DeclRefType>(extDeclRef.getDecl()->targetType))
+ if( request.semantics )
{
- if (auto aggDeclRef = targetDeclRef->declRef.as<AggTypeDecl>())
- {
- containerDeclRef = extDeclRef.Substitute(aggDeclRef);
- }
+ ensureDecl(request.semantics, extDeclRef.getDecl(), DeclCheckState::CanUseExtensionTargetType);
}
+
+ // If we are doing lookup from inside an `extension`
+ // declaration, then the `this` expression will have
+ // a type that uses the "target type" of the `extension`.
+ //
+ type = GetTargetType(extDeclRef);
}
+ else
+ {
+ assert(aggTypeDeclBaseRef.as<AggTypeDecl>());
+ type = DeclRefType::Create(session, aggTypeDeclBaseRef);
+ }
+
+ _lookUpMembersInType(session, name, type, request, result, &breadcrumb);
+ }
+ else
+ {
+ // The default case is when the scope doesn't represent a
+ // type or `extension` declaration, so we can look up members
+ // in that scope much more simply.
+ //
+ _lookUpDirectAndTransparentMembers(session, name, containerDeclRef, request, result, nullptr);
}
- DoLocalLookupImpl(
- session,
- name, containerDeclRef, request, result, breadcrumbs);
+ // Before we proceed up to the next outer scope to perform lookup
+ // again, we need to consider what the current scope tells us
+ // about how to interpret uses of `this`. For example, if
+ // we are inside a `[mutating]` method, then the implicit `this`
+ // that we use for lookup should be an l-value.
+ //
if( containerDeclRef.is<ConstructorDecl>() )
{
thisParameterMode = LookupResultItem::Breadcrumb::ThisParameterMode::Mutating;
@@ -546,16 +706,6 @@ void DoLookupImpl(
// If we run out of scopes, then we are done.
}
-LookupResult DoLookup(
- Session* session,
- Name* name,
- LookupRequest const& request)
-{
- LookupResult result;
- DoLookupImpl(session, name, request, result);
- return result;
-}
-
LookupResult lookUp(
Session* session,
SemanticsVisitor* semantics,
@@ -567,24 +717,9 @@ LookupResult lookUp(
request.semantics = semantics;
request.scope = scope;
request.mask = mask;
- return DoLookup(session, name, request);
-}
-
-// perform lookup within the context of a particular container declaration,
-// and do *not* look further up the chain
-LookupResult lookUpLocal(
- Session* session,
- SemanticsVisitor* semantics,
- Name* name,
- DeclRef<ContainerDecl> containerDeclRef,
- LookupMask mask)
-{
- LookupRequest request;
- request.semantics = semantics;
- request.mask = mask;
LookupResult result;
- DoLocalLookupImpl(session, name, containerDeclRef, request, result, nullptr);
+ _lookUpInScopes(session, name, request, result);
return result;
}
@@ -597,128 +732,6 @@ void lookUpMemberImpl(
BreadcrumbInfo* inBreadcrumbs,
LookupMask mask);
-// Perform lookup "through" the given constraint decl-ref,
-// which should show that `subType` is a sub-type of some
-// super-type (e.g., an interface).
-//
-void lookUpThroughConstraint(
- Session* session,
- SemanticsVisitor* semantics,
- Name* name,
- Type* subType,
- DeclRef<TypeConstraintDecl> constraintDeclRef,
- LookupResult& ioResult,
- BreadcrumbInfo* inBreadcrumbs,
- LookupMask mask)
-{
- // The super-type in the constraint (e.g., `Foo` in `T : Foo`)
- // will tell us a type we should use for lookup.
- //
- auto superType = GetSup(constraintDeclRef);
- //
- // We will go ahead and perform lookup using `superType`,
- // after dealing with some details.
-
- // If we are looking up through an interface type, then
- // we need to be sure that we add an appropriate
- // "this type" substitution here, since that needs to
- // be applied to any members we look up.
- //
- superType = maybeSpecializeInterfaceDeclRef(
- session,
- subType,
- superType,
- constraintDeclRef);
-
- // We need to track the indirection we took in lookup,
- // so that we can construct an appropriate AST on the other
- // side that includes the "upcase" from sub-type to super-type.
- //
- BreadcrumbInfo breadcrumb;
- breadcrumb.prev = inBreadcrumbs;
- breadcrumb.kind = LookupResultItem::Breadcrumb::Kind::Constraint;
- breadcrumb.declRef = constraintDeclRef;
-
- // TODO: Need to consider case where this might recurse infinitely (e.g.,
- // if an inheritance clause does something like `Bad<T> : Bad<Bad<T>>`.
- //
- // TODO: The even simpler thing we need to worry about here is that if
- // there is ever a "diamond" relationship in the inheritance hierarchy,
- // we might end up seeing the same interface via different "paths" and
- // we wouldn't want that to lead to overload-resolution failure.
- //
- lookUpMemberImpl(session, semantics, name, superType, ioResult, &breadcrumb, mask);
-}
-
-void lookUpMemberImpl(
- Session* session,
- SemanticsVisitor* semantics,
- Name* name,
- Type* type,
- LookupResult& ioResult,
- BreadcrumbInfo* inBreadcrumbs,
- LookupMask mask)
-{
- if (auto declRefType = as<DeclRefType>(type))
- {
- auto declRef = declRefType->declRef;
- if (declRef.as<AssocTypeDecl>() || declRef.as<GlobalGenericParamDecl>())
- {
- for (auto constraintDeclRef : getMembersOfType<TypeConstraintDecl>(declRef.as<ContainerDecl>()))
- {
- lookUpThroughConstraint(
- session,
- semantics,
- name,
- type,
- constraintDeclRef,
- ioResult,
- inBreadcrumbs,
- mask);
- }
- }
- else if (auto aggTypeDeclRef = declRef.as<AggTypeDecl>())
- {
- LookupRequest request;
- request.semantics = semantics;
-
- DoLocalLookupImpl(session, name, aggTypeDeclRef, request, ioResult, inBreadcrumbs);
- }
- else if (auto genericTypeParamDeclRef = declRef.as<GenericTypeParamDecl>())
- {
- auto genericDeclRef = genericTypeParamDeclRef.GetParent().as<GenericDecl>();
- assert(genericDeclRef);
-
- for(auto constraintDeclRef : getMembersOfType<GenericTypeConstraintDecl>(genericDeclRef))
- {
- // Does this constraint pertain to the type we are working on?
- //
- // We want constraints of the form `T : Foo` where `T` is the
- // generic parameter in question, and `Foo` is whatever we are
- // constraining it to.
- auto subType = GetSub(constraintDeclRef);
- auto subDeclRefType = as<DeclRefType>(subType);
- if(!subDeclRefType)
- continue;
- if(!subDeclRefType->declRef.Equals(genericTypeParamDeclRef))
- continue;
-
- lookUpThroughConstraint(
- session,
- semantics,
- name,
- type,
- constraintDeclRef,
- ioResult,
- inBreadcrumbs,
- mask);
- }
- }
-
- }
-
-}
-
LookupResult lookUpMember(
Session* session,
SemanticsVisitor* semantics,
@@ -726,8 +739,12 @@ LookupResult lookUpMember(
Type* type,
LookupMask mask)
{
+ LookupRequest request;
+ request.semantics = semantics;
+ request.mask = mask;
+
LookupResult result;
- lookUpMemberImpl(session, semantics, name, type, result, nullptr, mask);
+ _lookUpMembersInType(session, name, type, request, result, nullptr);
return result;
}
diff --git a/source/slang/slang-lookup.h b/source/slang/slang-lookup.h
index eaf51f1e5..f9e3839d9 100644
--- a/source/slang/slang-lookup.h
+++ b/source/slang/slang-lookup.h
@@ -24,15 +24,6 @@ LookupResult lookUp(
RefPtr<Scope> scope,
LookupMask mask = LookupMask::Default);
-// perform lookup within the context of a particular container declaration,
-// and do *not* look further up the chain
-LookupResult lookUpLocal(
- Session* session,
- SemanticsVisitor* semantics,
- Name* name,
- DeclRef<ContainerDecl> containerDeclRef,
- LookupMask mask = LookupMask::Default);
-
// Perform member lookup in the context of a type
LookupResult lookUpMember(
Session* session,
diff --git a/source/slang/slang-stdlib.cpp b/source/slang/slang-stdlib.cpp
index 551a911c8..b23d1970b 100644
--- a/source/slang/slang-stdlib.cpp
+++ b/source/slang/slang-stdlib.cpp
@@ -199,50 +199,50 @@ namespace Slang
}
}
- struct OpInfo { IntrinsicOp opCode; char const* opName; unsigned flags; };
+ struct OpInfo { IntrinsicOp opCode; char const* opName; char const* interface; unsigned flags; };
static const OpInfo unaryOps[] = {
- { kCompoundIntrinsicOp_Pos, "+", ARITHMETIC_MASK },
- { kIROp_Neg, "-", ARITHMETIC_MASK },
- { kIROp_Not, "!", BOOL_MASK | BOOL_RESULT },
- { kIROp_BitNot, "~", INT_MASK },
- { kCompoundIntrinsicOp_PreInc, "++", ARITHMETIC_MASK | ASSIGNMENT },
- { kCompoundIntrinsicOp_PreDec, "--", ARITHMETIC_MASK | ASSIGNMENT },
- { kCompoundIntrinsicOp_PostInc, "++", ARITHMETIC_MASK | ASSIGNMENT | POSTFIX },
- { kCompoundIntrinsicOp_PostDec, "--", ARITHMETIC_MASK | ASSIGNMENT | POSTFIX },
+ { kCompoundIntrinsicOp_Pos, "+", "__BuiltinArithmeticType", ARITHMETIC_MASK },
+ { kIROp_Neg, "-", "__BuiltinArithmeticType", ARITHMETIC_MASK },
+ { kIROp_Not, "!", nullptr, BOOL_MASK | BOOL_RESULT },
+ { kIROp_BitNot, "~", "__BuiltinIntegerType", INT_MASK },
+ { kCompoundIntrinsicOp_PreInc, "++", "__BuiltinArithmeticType", ARITHMETIC_MASK | ASSIGNMENT },
+ { kCompoundIntrinsicOp_PreDec, "--", "__BuiltinArithmeticType", ARITHMETIC_MASK | ASSIGNMENT },
+ { kCompoundIntrinsicOp_PostInc, "++", "__BuiltinArithmeticType", ARITHMETIC_MASK | ASSIGNMENT | POSTFIX },
+ { kCompoundIntrinsicOp_PostDec, "--", "__BuiltinArithmeticType", ARITHMETIC_MASK | ASSIGNMENT | POSTFIX },
};
static const OpInfo binaryOps[] = {
- { kIROp_Add, "+", ARITHMETIC_MASK },
- { kIROp_Sub, "-", ARITHMETIC_MASK },
- { kIROp_Mul, "*", ARITHMETIC_MASK },
- { kIROp_Div, "/", ARITHMETIC_MASK },
- { kIROp_IRem, "%", INT_MASK },
- { kIROp_FRem, "%", FLOAT_MASK },
- { kIROp_And, "&&", BOOL_MASK | BOOL_RESULT},
- { kIROp_Or, "||", BOOL_MASK | BOOL_RESULT },
- { kIROp_BitAnd, "&", LOGICAL_MASK },
- { kIROp_BitOr, "|", LOGICAL_MASK },
- { kIROp_BitXor, "^", LOGICAL_MASK },
- { kIROp_Lsh, "<<", INT_MASK },
- { kIROp_Rsh, ">>", INT_MASK },
- { kIROp_Eql, "==", ANY_MASK | BOOL_RESULT },
- { kIROp_Neq, "!=", ANY_MASK | BOOL_RESULT },
- { kIROp_Greater, ">", ARITHMETIC_MASK | BOOL_RESULT },
- { kIROp_Less, "<", ARITHMETIC_MASK | BOOL_RESULT },
- { kIROp_Geq, ">=", ARITHMETIC_MASK | BOOL_RESULT },
- { kIROp_Leq, "<=", ARITHMETIC_MASK | BOOL_RESULT },
- { kCompoundIntrinsicOp_AddAssign, "+=", ASSIGNMENT | ARITHMETIC_MASK },
- { kCompoundIntrinsicOp_SubAssign, "-=", ASSIGNMENT | ARITHMETIC_MASK },
- { kCompoundIntrinsicOp_MulAssign, "*=", ASSIGNMENT | ARITHMETIC_MASK },
- { kCompoundIntrinsicOp_DivAssign, "/=", ASSIGNMENT | ARITHMETIC_MASK },
- { kCompoundIntrinsicOp_IRemAssign, "%=", ASSIGNMENT | INT_MASK },
- { kCompoundIntrinsicOp_FRemAssign, "%=", ASSIGNMENT | FLOAT_MASK },
- { kCompoundIntrinsicOp_AndAssign, "&=", ASSIGNMENT | LOGICAL_MASK },
- { kCompoundIntrinsicOp_OrAssign, "|=", ASSIGNMENT | LOGICAL_MASK },
- { kCompoundIntrinsicOp_XorAssign, "^=", ASSIGNMENT | LOGICAL_MASK },
- { kCompoundIntrinsicOp_LshAssign, "<<=", ASSIGNMENT | INT_MASK },
- { kCompoundIntrinsicOp_RshAssign, ">>=", ASSIGNMENT | INT_MASK },
+ { kIROp_Add, "+", "__BuiltinArithmeticType", ARITHMETIC_MASK },
+ { kIROp_Sub, "-", "__BuiltinArithmeticType", ARITHMETIC_MASK },
+ { kIROp_Mul, "*", "__BuiltinArithmeticType", ARITHMETIC_MASK },
+ { kIROp_Div, "/", "__BuiltinArithmeticType", ARITHMETIC_MASK },
+ { kIROp_IRem, "%", "__BuiltinIntegerType", INT_MASK },
+ { kIROp_FRem, "%", "__BuiltinFloatingPointType", FLOAT_MASK },
+ { kIROp_And, "&&", nullptr, BOOL_MASK | BOOL_RESULT},
+ { kIROp_Or, "||", nullptr, BOOL_MASK | BOOL_RESULT },
+ { kIROp_BitAnd, "&", "__BuiltinLogicalType", LOGICAL_MASK },
+ { kIROp_BitOr, "|", "__BuiltinLogicalType", LOGICAL_MASK },
+ { kIROp_BitXor, "^", "__BuiltinLogicalType", LOGICAL_MASK },
+ { kIROp_Lsh, "<<", "__BuiltinIntegerType", INT_MASK },
+ { kIROp_Rsh, ">>", "__BuiltinIntegerType", INT_MASK },
+ { kIROp_Eql, "==", "__BuiltinType", ANY_MASK | BOOL_RESULT },
+ { kIROp_Neq, "!=", "__BuiltinType", ANY_MASK | BOOL_RESULT },
+ { kIROp_Greater, ">", "__BuiltinArithmeticType", ARITHMETIC_MASK | BOOL_RESULT },
+ { kIROp_Less, "<", "__BuiltinArithmeticType", ARITHMETIC_MASK | BOOL_RESULT },
+ { kIROp_Geq, ">=", "__BuiltinArithmeticType", ARITHMETIC_MASK | BOOL_RESULT },
+ { kIROp_Leq, "<=", "__BuiltinArithmeticType", ARITHMETIC_MASK | BOOL_RESULT },
+ { kCompoundIntrinsicOp_AddAssign, "+=", "__BuiltinArithmeticType", ASSIGNMENT | ARITHMETIC_MASK },
+ { kCompoundIntrinsicOp_SubAssign, "-=", "__BuiltinArithmeticType", ASSIGNMENT | ARITHMETIC_MASK },
+ { kCompoundIntrinsicOp_MulAssign, "*=", "__BuiltinArithmeticType", ASSIGNMENT | ARITHMETIC_MASK },
+ { kCompoundIntrinsicOp_DivAssign, "/=", "__BuiltinArithmeticType", ASSIGNMENT | ARITHMETIC_MASK },
+ { kCompoundIntrinsicOp_IRemAssign, "%=", "__BuiltinIntegerType", ASSIGNMENT | INT_MASK },
+ { kCompoundIntrinsicOp_FRemAssign, "%=", "__BuiltinFloatingPointType", ASSIGNMENT | FLOAT_MASK },
+ { kCompoundIntrinsicOp_AndAssign, "&=", "__BuiltinLogicalType", ASSIGNMENT | LOGICAL_MASK },
+ { kCompoundIntrinsicOp_OrAssign, "|=", "__BuiltinLogicalType", ASSIGNMENT | LOGICAL_MASK },
+ { kCompoundIntrinsicOp_XorAssign, "^=", "__BuiltinLogicalType", ASSIGNMENT | LOGICAL_MASK },
+ { kCompoundIntrinsicOp_LshAssign, "<<=", "__BuiltinIntegerType", ASSIGNMENT | INT_MASK },
+ { kCompoundIntrinsicOp_RshAssign, ">>=", "__BuiltinIntegerType", ASSIGNMENT | INT_MASK },
};
String Session::getCoreLibraryCode()
diff --git a/source/slang/slang-syntax.cpp b/source/slang/slang-syntax.cpp
index cc2cf46a1..d479ba32a 100644
--- a/source/slang/slang-syntax.cpp
+++ b/source/slang/slang-syntax.cpp
@@ -1548,7 +1548,18 @@ void Type::accept(IValVisitor* visitor, void* extra)
if (auto thisTypeSubst = as<ThisTypeSubstitution>(subst))
{
- return witness->EqualsVal(thisTypeSubst->witness);
+ // For our purposes, two this-type substitutions are
+ // equivalent if they have the same type as `This`,
+ // even if the specific witness values they use
+ // might differ.
+ //
+ if(this->interfaceDecl != thisTypeSubst->interfaceDecl)
+ return false;
+
+ if(!this->witness->sub->Equals(thisTypeSubst->witness->sub))
+ return false;
+
+ return true;
}
return false;
}
diff --git a/source/slang/slang-syntax.h b/source/slang/slang-syntax.h
index f8f506236..9c3945b4d 100644
--- a/source/slang/slang-syntax.h
+++ b/source/slang/slang-syntax.h
@@ -1168,8 +1168,6 @@ namespace Slang
// used at all, to avoid allocation.
List<LookupResultItem> items;
- HashSet<DeclRef<ContainerDecl>> lookedupDecls;
-
// Was at least one result found?
bool isValid() const { return item.declRef.getDecl() != nullptr; }