From 7349dc5cff49cf22c82eb912813e47f30cd7a757 Mon Sep 17 00:00:00 2001 From: Julius Ikkala Date: Sat, 28 Jun 2025 05:39:24 +0300 Subject: Minimal optional constraints (#7422) * Parse optional witness syntax * Allow failing optional constraint * Make `is` work with optional constraint * Allow using optional constraint in checked if statements * Fix tests * Make it work with structs * Fix MSVC build error * Disallow using `as` with optional constraints * Update test to match split is/as errors * Add tests * Fix uninitialized variables in tests * Add tests of incorrect uses & fix related bugs * Mention optional constraints in docs * format code * Fix type unification with NoneWitness * Fix formatting --------- Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com> Co-authored-by: Nathan V. Morrical --- source/slang/slang-check-constraint.cpp | 46 +++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 11 deletions(-) (limited to 'source/slang/slang-check-constraint.cpp') diff --git a/source/slang/slang-check-constraint.cpp b/source/slang/slang-check-constraint.cpp index 3020554c8..7c55c440c 100644 --- a/source/slang/slang-check-constraint.cpp +++ b/source/slang/slang-check-constraint.cpp @@ -360,9 +360,12 @@ DeclRef SemanticsVisitor::trySolveConstraintSystem( for (auto constraintDeclRef : getMembersOfType(m_astBuilder, genericDeclRef)) { + ValUnificationContext unificationContext; + unificationContext.optionalConstraint = + constraintDeclRef.getDecl()->hasModifier(); if (!TryUnifyTypes( *system, - ValUnificationContext(), + unificationContext, getSub(m_astBuilder, constraintDeclRef), getSup(m_astBuilder, constraintDeclRef))) return DeclRef(); @@ -487,8 +490,11 @@ DeclRef SemanticsVisitor::trySolveConstraintSystem( auto joinType = TryJoinTypes(system, type, cType); if (!joinType) { - // failure! - return DeclRef(); + if (c.isOptional) + joinType = type; + else + // failure! + return DeclRef(); } type = QualType(joinType, type.isLeftValue || cType.isLeftValue); } @@ -696,12 +702,22 @@ DeclRef SemanticsVisitor::trySolveConstraintSystem( subTypeWitness = nullptr; } - if (subTypeWitness) + bool witnessIsOptional = isWitnessUncheckedOptional(subTypeWitness); + bool constraintIsOptional = constraintDecl->hasModifier(); + + if (subTypeWitness && (!witnessIsOptional || constraintIsOptional)) { // We found a witness, so it will become an (implicit) argument. args.add(subTypeWitness); outBaseCost += subTypeWitness->getOverloadResolutionCost(); } + else if (!subTypeWitness && constraintIsOptional) + { + // Optional witness failed to resolve; not an error. + auto noneWitness = m_astBuilder->getOrCreate(); + args.add(noneWitness); + outBaseCost += kConversionCost_FailedOptionalConstraint; + } else { // No witness was found, so the inference will now fail. @@ -851,13 +867,20 @@ bool SemanticsVisitor::TryUnifyVals( // Two subtype witnesses can be unified if they exist (non-null) and // prove that some pair of types are subtypes of types that can be unified. // - if (auto fstWit = as(fst)) - { - if (auto sndWit = as(snd)) - { - return TryUnifyTypes(constraints, unifyCtx, fstWit->getSup(), sndWit->getSup()); - } - } + const auto fstSubtypeWitness = as(fst); + const auto sndSubtypeWitness = as(snd); + const auto fstNoneWitness = as(fst); + const auto sndNoneWitness = as(snd); + if (fstSubtypeWitness && sndSubtypeWitness) + return TryUnifyTypes( + constraints, + unifyCtx, + fstSubtypeWitness->getSup(), + sndSubtypeWitness->getSup()); + else if (fstNoneWitness && sndNoneWitness) + return true; + else if ((fstNoneWitness && sndSubtypeWitness) || (fstSubtypeWitness && sndNoneWitness)) + return false; SLANG_UNIMPLEMENTED_X("value unification case"); @@ -946,6 +969,7 @@ bool SemanticsVisitor::TryUnifyTypeParam( constraint.indexInPack = unificationContext.indexInTypePack; constraint.val = type; constraint.isUsedAsLValue = type.isLeftValue; + constraint.isOptional = unificationContext.optionalConstraint; constraints.constraints.add(constraint); return true; -- cgit v1.2.3