summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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
-rw-r--r--tests/diagnostics/enum-implicit-conversion.slang.expected4
-rw-r--r--tests/front-end/diamond.slang32
17 files changed, 1797 insertions, 910 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; }
diff --git a/tests/diagnostics/enum-implicit-conversion.slang.expected b/tests/diagnostics/enum-implicit-conversion.slang.expected
index 1fc0c3dc6..c102c6012 100644
--- a/tests/diagnostics/enum-implicit-conversion.slang.expected
+++ b/tests/diagnostics/enum-implicit-conversion.slang.expected
@@ -7,8 +7,8 @@ tests/diagnostics/enum-implicit-conversion.slang(34): note: explicit conversion
tests/diagnostics/enum-implicit-conversion.slang(35): error 30019: expected an expression of type 'uint', got 'Color'
tests/diagnostics/enum-implicit-conversion.slang(35): note: explicit conversion from 'Color' to 'uint' is possible
tests/diagnostics/enum-implicit-conversion.slang(42): error 39999: ambiguous call to overloaded operation with arguments of type (Color)
-tests/diagnostics/enum-implicit-conversion.slang(18): note 39999: candidate: foo(uint)
-tests/diagnostics/enum-implicit-conversion.slang(17): note 39999: candidate: foo(int)
+tests/diagnostics/enum-implicit-conversion.slang(18): note 39999: candidate: func foo(uint) -> int
+tests/diagnostics/enum-implicit-conversion.slang(17): note 39999: candidate: func foo(int) -> int
}
standard output = {
}
diff --git a/tests/front-end/diamond.slang b/tests/front-end/diamond.slang
new file mode 100644
index 000000000..bcb1bbd43
--- /dev/null
+++ b/tests/front-end/diamond.slang
@@ -0,0 +1,32 @@
+// diamond.slang
+
+//TEST:SIMPLE:
+
+// Test to confirm that Slang can handle a "diamond"
+// multiple inheritance pattern between interfaces,
+// without having lookup ambiguity issues at use
+// sites.
+
+interface A { float getA(); }
+
+interface B : A {}
+
+interface C : A {}
+
+interface D : B, C {}
+
+float doIt<T : D>(T value)
+{
+ return value.getA();
+}
+
+struct Thing : D
+{
+ float getA() { return 0.0f; }
+}
+
+float test()
+{
+ Thing thing;
+ return doIt(thing);
+} \ No newline at end of file