diff options
| -rw-r--r-- | source/slang/check.cpp | 140 | ||||
| -rw-r--r-- | source/slang/core.meta.slang | 30 | ||||
| -rw-r--r-- | source/slang/core.meta.slang.h | 30 | ||||
| -rw-r--r-- | source/slang/lookup.cpp | 8 | ||||
| -rw-r--r-- | source/slang/lower-to-ir.cpp | 5 | ||||
| -rw-r--r-- | source/slang/lower.cpp | 17 | ||||
| -rw-r--r-- | tests/bugs/array-size-static-const.hlsl | 14 | ||||
| -rw-r--r-- | tests/bugs/empty.slang | 5 | ||||
| -rw-r--r-- | tests/bugs/implicit-conversion-binary-op.hlsl | 16 | ||||
| -rw-r--r-- | tests/bugs/import-overload-error.hlsl | 19 | ||||
| -rw-r--r-- | tests/bugs/import-overload-error.slang | 4 | ||||
| -rw-r--r-- | tests/bugs/uav-write-index.hlsl | 31 | ||||
| -rw-r--r-- | tests/bugs/vec-init-list.hlsl | 19 |
13 files changed, 292 insertions, 46 deletions
diff --git a/source/slang/check.cpp b/source/slang/check.cpp index 73d464d95..b35dd2662 100644 --- a/source/slang/check.cpp +++ b/source/slang/check.cpp @@ -671,6 +671,59 @@ namespace Slang // we will collect the new arguments here List<RefPtr<Expr>> coercedArgs; + if (auto toVecType = toType->As<VectorExpressionType>()) + { + auto toElementCount = toVecType->elementCount; + auto toElementType = toVecType->elementType; + + UInt elementCount = 0; + if (auto constElementCount = toElementCount.As<ConstantIntVal>()) + { + elementCount = (UInt) constElementCount->value; + } + else + { + // We don't know the element count statically, + // so what are we supposed to be doing? + elementCount = fromInitializerListExpr->args.Count(); + } + + // TODO: need to check that the element count + // for the vector type matches the argument + // count for the initializer list, or else + // fix them up to match. + + for(auto arg : fromInitializerListExpr->args) + { + RefPtr<Expr> coercedArg; + ConversionCost argCost; + + bool argResult = TryCoerceImpl( + toElementType, + outToExpr ? &coercedArg : nullptr, + arg->type, + arg, + outCost ? &argCost : nullptr); + + // No point in trying further if any argument fails + if(!argResult) + return false; + + // TODO(tfoley): what to do with cost? + // This only matters if/when we allow an initializer list as an argument to + // an overloaded call. + + if( outToExpr ) + { + coercedArgs.Add(coercedArg); + } + } + } + // + // TODO(tfoley): How to handle matrices here? + // Should they expect individual scalars, or support + // vectors for the rows? + // if(auto toDeclRefType = toType->As<DeclRefType>()) { auto toTypeDeclRef = toDeclRefType->declRef; @@ -804,9 +857,6 @@ namespace Slang OverloadResolveContext overloadContext; - List<RefPtr<Expr>> args; - args.Add(fromExpr); - overloadContext.disallowNestedConversions = true; overloadContext.argCount = 1; overloadContext.argTypes = &fromType; @@ -884,7 +934,45 @@ namespace Slang if(outToExpr) { + // The logic here is a bit ugly, to deal with the fact that + // `CompleteOverloadCandidate` will, left to its own devices, + // construct a vanilla `InvokeExpr` to represent the call + // to the initializer we found, while we *want* it to + // create some variety of `ImplicitCastExpr`. + // + // Now, it just so happens that `CompleteOverloadCandidate` + // will use the "original" expression if one is available, + // so we'll create one and initialize it here. + // We fill in the location and arguments, but not the + // base expression (the callee), since that will come + // from the selected overload candidate. + // + auto castExpr = createImplicitCastExpr(); + castExpr->loc = fromExpr->loc; + castExpr->Arguments.Add(fromExpr); + // + // Next we need to set our cast expression as the "original" + // expression and then complete the overload process. + // + overloadContext.originalExpr = castExpr; *outToExpr = CompleteOverloadCandidate(overloadContext, *overloadContext.bestCandidate); + // + // However, the above isn't *quite* enough, because + // the process of completing the overload candidate + // might overwrite the argument list that was passed + // in to overload resolution, and in this case that + // "argument list" was just a pointer to `fromExpr`. + // + // That means we need to clear the argument list and + // reload it from `fromExpr` to make sure that we + // got the arguments *after* any transformations + // were applied. + // For right now this probably doesn't matter, + // because we don't allow nested implicit conversions, + // but I'd rather play it safe. + // + castExpr->Arguments.Clear(); + castExpr->Arguments.Add(fromExpr); } return true; @@ -907,22 +995,26 @@ namespace Slang outCost); } - RefPtr<Expr> CreateImplicitCastExpr( - RefPtr<Type> toType, - RefPtr<Expr> fromExpr) + RefPtr<TypeCastExpr> createImplicitCastExpr() { - // In "rewrite" mode, we will generate a different syntax node - // to indicate that this type-cast was implicitly generated - // by the compiler, and shouldn't appear in the output code. - RefPtr<TypeCastExpr> castExpr; if (isRewriteMode()) { - castExpr = new HiddenImplicitCastExpr(); + // In "rewrite" mode, we will generate a different syntax node + // to indicate that this type-cast was implicitly generated + // by the compiler, and shouldn't appear in the output code. + return new HiddenImplicitCastExpr(); } else { - castExpr = new ImplicitCastExpr(); + return new ImplicitCastExpr(); } + } + + RefPtr<Expr> CreateImplicitCastExpr( + RefPtr<Type> toType, + RefPtr<Expr> fromExpr) + { + RefPtr<TypeCastExpr> castExpr = createImplicitCastExpr(); auto typeType = new TypeType(); typeType->type = toType; @@ -2005,7 +2097,17 @@ namespace Slang auto funcDeclRef = funcDeclRefExpr->declRef; auto intrinsicMod = funcDeclRef.getDecl()->FindModifier<IntrinsicOpModifier>(); - if (!intrinsicMod) return nullptr; + if (!intrinsicMod) + { + // We can't constant fold anything that doesn't map to a builtin + // operation right now. + // + // TODO: we should really allow constant-folding for anything + // that can be lowerd to our bytecode... + return nullptr; + } + + // Let's not constant-fold operations with more than a certain number of arguments, for simplicity static const int kMaxArgs = 8; @@ -2188,16 +2290,16 @@ namespace Slang } } - if (auto invokeExpr = dynamic_cast<InvokeExpr*>(expr)) + if(auto castExpr = dynamic_cast<TypeCastExpr*>(expr)) { - auto val = TryConstantFoldExpr(invokeExpr); - if (val) + auto val = TryConstantFoldExpr(castExpr->Arguments[0].Ptr()); + if(val) return val; } - else if(auto castExpr = dynamic_cast<TypeCastExpr*>(expr)) + else if (auto invokeExpr = dynamic_cast<InvokeExpr*>(expr)) { - auto val = TryConstantFoldExpr(castExpr->Arguments[0].Ptr()); - if(val) + auto val = TryConstantFoldExpr(invokeExpr); + if (val) return val; } diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang index 3755c51c7..3f1a33edc 100644 --- a/source/slang/core.meta.slang +++ b/source/slang/core.meta.slang @@ -108,6 +108,9 @@ 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"; // TODO: Probably need to do similar @@ -190,13 +193,6 @@ for (int N = 2; N <= 4; ++N) sb << ");\n"; } - // initialize from another vector of the same size - // - // TODO(tfoley): this overlaps with implicit conversions. - // We should look for a way that we can define implicit - // conversions directly in the stdlib instead... - sb << "__generic<U> __init(vector<U," << N << ">);\n"; - // Initialize from two vectors, of size M and N-M for(int M = 2; M <= (N-2); ++M) { @@ -230,16 +226,20 @@ for (int tt = 0; tt < kBaseTypeCount; ++tt) { if(kBaseTypes[ff].tag == BaseType::Void) continue; - // We need a constructor to make a vector from a scalar - // of another type. if( tt != ff ) { auto cost = getBaseTypeConversionCost( kBaseTypes[tt], kBaseTypes[ff]); - cost += kConversionCost_ScalarToVector; + // Implicit conversion from a vector of the same + // size, but different element type. + sb << " __implicit_conversion(" << cost << ")\n"; + sb << " __init(vector<" << kBaseTypes[ff].name << ",N> value);\n"; + + // Constructor to make a vector from a scalar of another type. + cost += kConversionCost_ScalarToVector; sb << " __implicit_conversion(" << cost << ")\n"; sb << " __init(" << kBaseTypes[ff].name << " value);\n"; } @@ -557,8 +557,16 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt) if(baseShape != TextureType::ShapeCube) { + // TODO: In the case where `access` includes writeability, + // this should have both `get` and `set` accessors. + // subscript operator - sb << "__intrinsic_op __subscript(uint" << kBaseTextureTypes[tt].coordCount + isArray << " location) -> T;\n"; + sb << "__intrinsic_op __subscript(uint"; + if(kBaseTextureTypes[tt].coordCount + isArray > 1) + { + sb << kBaseTextureTypes[tt].coordCount + isArray; + } + sb << " location) -> T;\n"; } if( !isMultisample ) diff --git a/source/slang/core.meta.slang.h b/source/slang/core.meta.slang.h index 864196944..900d05b61 100644 --- a/source/slang/core.meta.slang.h +++ b/source/slang/core.meta.slang.h @@ -110,6 +110,9 @@ 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"; // TODO: Probably need to do similar @@ -193,13 +196,6 @@ for (int N = 2; N <= 4; ++N) sb << ");\n"; } - // initialize from another vector of the same size - // - // TODO(tfoley): this overlaps with implicit conversions. - // We should look for a way that we can define implicit - // conversions directly in the stdlib instead... - sb << "__generic<U> __init(vector<U," << N << ">);\n"; - // Initialize from two vectors, of size M and N-M for(int M = 2; M <= (N-2); ++M) { @@ -233,16 +229,20 @@ for (int tt = 0; tt < kBaseTypeCount; ++tt) { if(kBaseTypes[ff].tag == BaseType::Void) continue; - // We need a constructor to make a vector from a scalar - // of another type. if( tt != ff ) { auto cost = getBaseTypeConversionCost( kBaseTypes[tt], kBaseTypes[ff]); - cost += kConversionCost_ScalarToVector; + // Implicit conversion from a vector of the same + // size, but different element type. + sb << " __implicit_conversion(" << cost << ")\n"; + sb << " __init(vector<" << kBaseTypes[ff].name << ",N> value);\n"; + + // Constructor to make a vector from a scalar of another type. + cost += kConversionCost_ScalarToVector; sb << " __implicit_conversion(" << cost << ")\n"; sb << " __init(" << kBaseTypes[ff].name << " value);\n"; } @@ -560,8 +560,16 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt) if(baseShape != TextureType::ShapeCube) { + // TODO: In the case where `access` includes writeability, + // this should have both `get` and `set` accessors. + // subscript operator - sb << "__intrinsic_op __subscript(uint" << kBaseTextureTypes[tt].coordCount + isArray << " location) -> T;\n"; + sb << "__intrinsic_op __subscript(uint"; + if(kBaseTextureTypes[tt].coordCount + isArray > 1) + { + sb << kBaseTextureTypes[tt].coordCount + isArray; + } + sb << " location) -> T;\n"; } if( !isMultisample ) diff --git a/source/slang/lookup.cpp b/source/slang/lookup.cpp index ccdbe8fbf..95a1b3326 100644 --- a/source/slang/lookup.cpp +++ b/source/slang/lookup.cpp @@ -40,6 +40,9 @@ void buildMemberDictionary(ContainerDecl* decl) decl->memberDictionary.Clear(); decl->transparentMembers.Clear(); + // are we a generic? + GenericDecl* genericDecl = dynamic_cast<GenericDecl*>(decl); + for (auto m : decl->Members) { auto name = m->getName(); @@ -56,6 +59,11 @@ void buildMemberDictionary(ContainerDecl* decl) if (!name) continue; + // Ignore the "inner" member of a generic declaration + if (genericDecl && m == genericDecl->inner) + continue; + + m->nextInContainerWithSameName = nullptr; Decl* next = nullptr; diff --git a/source/slang/lower-to-ir.cpp b/source/slang/lower-to-ir.cpp index b9307b007..2f3456d0c 100644 --- a/source/slang/lower-to-ir.cpp +++ b/source/slang/lower-to-ir.cpp @@ -1452,11 +1452,6 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo> return ensureDecl(context, expr->declRef); } - LoweredValInfo visitTypeCastExpr(TypeCastExpr* expr) - { - SLANG_UNIMPLEMENTED_X("codegen for type cast expression"); - } - LoweredValInfo visitSelectExpr(SelectExpr* expr) { SLANG_UNIMPLEMENTED_X("codegen for select expression"); diff --git a/source/slang/lower.cpp b/source/slang/lower.cpp index 6d848062c..b620a7837 100644 --- a/source/slang/lower.cpp +++ b/source/slang/lower.cpp @@ -956,6 +956,23 @@ struct LoweringVisitor return LoweredExpr(loweredExpr); } + LoweredExpr visitOverloadedExpr( + OverloadedExpr* expr) + { + // The presence of an overloaded expression in the output + // means that some amount of semantic checking failed. + // Thus we don't need to worry about semantically transforming + // the expression itself, but we *do* want to ensure that any + // of the declarations that the user might have been referring + // to get lowered so they will appear in the output. + for (auto item : expr->lookupResult2.items) + { + translateDeclRef(item.declRef); + } + + return expr; + } + Name* getName(String const& text) { return shared->compileRequest->getNamePool()->getName(text); diff --git a/tests/bugs/array-size-static-const.hlsl b/tests/bugs/array-size-static-const.hlsl new file mode 100644 index 000000000..fe15d402d --- /dev/null +++ b/tests/bugs/array-size-static-const.hlsl @@ -0,0 +1,14 @@ +// array-size-static-const.hlsl +//TEST:COMPARE_HLSL: -profile cs_5_0 -target dxbc-assembly + +// The bug in this case is that were have a (hidden) +// cast from the `uint` constant to `int` to get +// the size of the array, and this cast was tripping +// up the constant-folding logic. + +static const uint n = 16; +groupshared float b[n]; + +[numthreads(1,1,1)] +void main() +{} diff --git a/tests/bugs/empty.slang b/tests/bugs/empty.slang new file mode 100644 index 000000000..ac4b3b7ca --- /dev/null +++ b/tests/bugs/empty.slang @@ -0,0 +1,5 @@ +//TEST_IGNORE_FILE: +// empty.slang + +// This is just an empty file so that tets +// that need to `__import` something can. diff --git a/tests/bugs/implicit-conversion-binary-op.hlsl b/tests/bugs/implicit-conversion-binary-op.hlsl new file mode 100644 index 000000000..75ff737da --- /dev/null +++ b/tests/bugs/implicit-conversion-binary-op.hlsl @@ -0,0 +1,16 @@ +// implicit-conversion-binary-op.hlsl +//TEST:COMPARE_HLSL: -profile ps_5_0 -target dxbc-assembly + +// Make sure that we can pick resolve the right overload +// to call when applying a binary operator to vectors +// with different element types. We should pick +// the "better" of the two element types, and not +// get an ambiguity error. + +float4 main( + float4 a : A, + uint4 b : B + ) : SV_Target +{ + return a * b; +} diff --git a/tests/bugs/import-overload-error.hlsl b/tests/bugs/import-overload-error.hlsl new file mode 100644 index 000000000..328bb5b26 --- /dev/null +++ b/tests/bugs/import-overload-error.hlsl @@ -0,0 +1,19 @@ +//TEST:COMPARE_HLSL: -profile cs_5_0 -target dxbc-assembly -no-checking + +#ifdef __SLANG__ +__import import_overload_error; +#else + +void foo(int a) {} +void foo(float b) {} + +#endif + +void main() +{ +// Note(tfoley): futzing around with tokens to +// make sure error message gets reported at a +// consistent location between languages. +int a; +foo(); +} diff --git a/tests/bugs/import-overload-error.slang b/tests/bugs/import-overload-error.slang new file mode 100644 index 000000000..e52ce78bb --- /dev/null +++ b/tests/bugs/import-overload-error.slang @@ -0,0 +1,4 @@ +//TEST_IGNORE_FILE: + +void foo(int a) {} +void foo(float b) {} diff --git a/tests/bugs/uav-write-index.hlsl b/tests/bugs/uav-write-index.hlsl new file mode 100644 index 000000000..667c73e89 --- /dev/null +++ b/tests/bugs/uav-write-index.hlsl @@ -0,0 +1,31 @@ +//TEST:COMPARE_HLSL: -profile cs_5_0 -target dxbc-assembly -no-checking + +// Make sure we handle complex UAV write patterns + +// Force import of Slang to ensure that some +// checking takes place: +#ifdef __SLANG__ +__import empty; +#endif + +struct Bar +{ + uint bar; +}; + +RWStructuredBuffer<Bar> gUAV : register(u0); + +void foo(RWTexture1D<float2> uav) +{ + uint index = gUAV.IncrementCounter(); + gUAV[index].bar = 1; + uav[index] = float2(0,0); +} + +RWTexture1D<float2> gUAV2 : register(u1); + +[numthreads(1,1,1)] +void main() +{ + foo(gUAV2); +} diff --git a/tests/bugs/vec-init-list.hlsl b/tests/bugs/vec-init-list.hlsl new file mode 100644 index 000000000..be1bc5c6f --- /dev/null +++ b/tests/bugs/vec-init-list.hlsl @@ -0,0 +1,19 @@ +//TEST:COMPARE_HLSL: -profile vs_5_0 -target dxbc-assembly + +// Check handling of initializer list for vector + +cbuffer C : register(b0) +{ + float4 a; +}; + +float w0(float x) { return x; } +float w1(float x) { return x; } +float w2(float x) { return x; } +float w3(float x) { return x; } + +float4 main() : SV_Position +{ + float4 wx = { w0(a.x), w1(a.x), w2(a.x), w3(a.x), }; + return wx; +} |
