diff options
Diffstat (limited to 'source/slang/slang-check-decl.cpp')
| -rw-r--r-- | source/slang/slang-check-decl.cpp | 107 |
1 files changed, 92 insertions, 15 deletions
diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp index d6b50e999..77a799f1c 100644 --- a/source/slang/slang-check-decl.cpp +++ b/source/slang/slang-check-decl.cpp @@ -365,9 +365,14 @@ struct SemanticsDeclHeaderVisitor : public SemanticsDeclVisitorBase, void visitGenericTypeConstraintDecl(GenericTypeConstraintDecl* decl); + void checkGenericTypeEqualityConstraintSubType(GenericTypeConstraintDecl* decl); + void visitTypeCoercionConstraintDecl(TypeCoercionConstraintDecl* decl); - void validateGenericConstraintSubType(GenericTypeConstraintDecl* decl, TypeExp type); + bool validateGenericConstraintSubType( + GenericTypeConstraintDecl* decl, + TypeExp type, + DiagnosticSink* sink = nullptr); void checkForwardReferencesInGenericConstraint(GenericTypeConstraintDecl* decl); @@ -3250,10 +3255,25 @@ bool isProperConstraineeType(Type* type) return true; } -void SemanticsDeclHeaderVisitor::validateGenericConstraintSubType( +bool SemanticsDeclHeaderVisitor::validateGenericConstraintSubType( GenericTypeConstraintDecl* decl, - TypeExp type) + TypeExp type, + DiagnosticSink* sink) { + auto diagnose = [&]() + { + if (sink) + { + if (decl->isEqualityConstraint) + { + sink->diagnose(type.exp, Diagnostics::invalidEqualityConstraintSubType, type); + } + else + { + sink->diagnose(type.exp, Diagnostics::invalidConstraintSubType, type); + } + } + }; // Validate that the sub type of a constraint is in valid form. // if (auto subDeclRef = isDeclRefTypeOf<Decl>(type.type)) @@ -3261,7 +3281,7 @@ void SemanticsDeclHeaderVisitor::validateGenericConstraintSubType( if (subDeclRef.getDecl()->parentDecl == decl->parentDecl) { // OK, sub type is one of the generic parameter type. - return; + return true; } if (as<GenericDecl>(decl->parentDecl)) { @@ -3272,8 +3292,8 @@ void SemanticsDeclHeaderVisitor::validateGenericConstraintSubType( auto dependentGeneric = getShared()->getDependentGenericParent(subDeclRef); if (dependentGeneric.getDecl() != decl->parentDecl) { - getSink()->diagnose(type.exp, Diagnostics::invalidConstraintSubType, type); - return; + diagnose(); + return false; } } else if (as<AssocTypeDecl>(decl->parentDecl)) @@ -3291,8 +3311,8 @@ void SemanticsDeclHeaderVisitor::validateGenericConstraintSubType( auto lookupDeclRef = as<LookupDeclRef>(subDeclRef.declRefBase); if (!lookupDeclRef) { - getSink()->diagnose(type.exp, Diagnostics::invalidConstraintSubType, type); - return; + diagnose(); + return false; } // We allow `associatedtype T where This.T : ...`. @@ -3301,24 +3321,25 @@ void SemanticsDeclHeaderVisitor::validateGenericConstraintSubType( // if (lookupDeclRef->getDecl()->parentDecl == decl->parentDecl || lookupDeclRef->getDecl() == decl->parentDecl) - return; + return true; auto baseType = as<Type>(lookupDeclRef->getLookupSource()); if (!baseType) { - getSink()->diagnose(type.exp, Diagnostics::invalidConstraintSubType, type); - return; + diagnose(); + return false; } type.type = baseType; - validateGenericConstraintSubType(decl, type); + return validateGenericConstraintSubType(decl, type, sink); } } if (!isProperConstraineeType(type.type)) { // It is meaningless for certain types to be used in type constraints. // For example, `IFoo<T>` should not appear as the left-hand-side of a generic constraint. - getSink()->diagnose(type.exp, Diagnostics::invalidConstraintSubType, type); - return; + diagnose(); + return false; } + return true; } // General utility function to collect all referenced declarations from a value @@ -3443,9 +3464,9 @@ void SemanticsDeclHeaderVisitor::visitGenericTypeConstraintDecl(GenericTypeConst // Check for forward references in generic constraints after type translation checkForwardReferencesInGenericConstraint(decl); - validateGenericConstraintSubType(decl, decl->sub); if (decl->isEqualityConstraint) { + checkGenericTypeEqualityConstraintSubType(decl); if (!isProperConstraineeType(decl->sup) && !as<ErrorType>(decl->sup.type)) { getSink()->diagnose( @@ -3456,6 +3477,7 @@ void SemanticsDeclHeaderVisitor::visitGenericTypeConstraintDecl(GenericTypeConst } else { + validateGenericConstraintSubType(decl, decl->sub, getSink()); if (!isValidGenericConstraintType(decl->sup) && !as<ErrorType>(decl->sup.type)) { getSink()->diagnose( @@ -3467,6 +3489,61 @@ void SemanticsDeclHeaderVisitor::visitGenericTypeConstraintDecl(GenericTypeConst } } +ContainerDecl* findDeclsLowestCommonAncestor(Decl*& a, Decl*& b); +int compareDecls(Decl* lhs, Decl* rhs); + +void SemanticsDeclHeaderVisitor::checkGenericTypeEqualityConstraintSubType( + GenericTypeConstraintDecl* decl) +{ + auto checkAndCompare = [&]() -> int + { + bool subOk = validateGenericConstraintSubType(decl, decl->sub); + bool supOk = validateGenericConstraintSubType(decl, decl->sup); + + if (subOk != supOk) // Only one is qualified + { + return int(supOk) - int(subOk); + } + else if (!(subOk || supOk)) + { + getSink()->diagnose(decl, Diagnostics::noValidEqualityConstraintSubType); + // Re-run the validation to emit the diagnostic this time + validateGenericConstraintSubType(decl, decl->sub, getSink()); + validateGenericConstraintSubType(decl, decl->sup, getSink()); + return -1; + } + // Both sub and sup are qualified + // For example: + // __generic <A : IA, B : IB> + // where A::T == B::T + // Sort them by declaration order in the generic (A > B) + + Decl* subAncestor = as<DeclRefType>(decl->sub.type)->getDeclRef().getDecl(); + Decl* supAncestor = as<DeclRefType>(decl->sup.type)->getDeclRef().getDecl(); + auto ancestor = findDeclsLowestCommonAncestor(subAncestor, supAncestor); + if (!ancestor) + { + return compareDecls(subAncestor, supAncestor); + } + + auto subIndex = ancestor->getMembers().binarySearch(subAncestor); + auto supIndex = ancestor->getMembers().binarySearch(supAncestor); + + return int(supIndex - subIndex); + }; + + int cmp = checkAndCompare(); + if (cmp > 0) + { + Swap(decl->sub, decl->sup); + } + else if (cmp == 0 && decl->sub != decl->sup) + { + // The comparison was not fully handled for this case. + getSink()->diagnose(decl, Diagnostics::failedEqualityConstraintCanonicalOrder); + } +} + void SemanticsDeclHeaderVisitor::visitGenericTypeParamDecl(GenericTypeParamDecl* decl) { // TODO: could probably push checking the default value |
