summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/slang/slang-check-conformance.cpp6
-rw-r--r--source/slang/slang-check-decl.cpp1414
-rw-r--r--source/slang/slang-check-expr.cpp37
-rw-r--r--source/slang/slang-check-impl.h391
-rw-r--r--source/slang/slang-check-modifier.cpp8
-rw-r--r--source/slang/slang-check-overload.cpp10
-rw-r--r--source/slang/slang-check-shader.cpp15
-rw-r--r--source/slang/slang-check-stmt.cpp144
-rw-r--r--source/slang/slang-check-type.cpp11
-rw-r--r--source/slang/slang-check.cpp31
-rw-r--r--source/slang/slang-check.h1
-rw-r--r--source/slang/slang-lookup.cpp20
-rw-r--r--source/slang/slang-lookup.h2
-rw-r--r--source/slang/slang-syntax-base-defs.h6
-rw-r--r--source/slang/slang-syntax.cpp10
-rw-r--r--source/slang/slang-syntax.h119
16 files changed, 1242 insertions, 983 deletions
diff --git a/source/slang/slang-check-conformance.cpp b/source/slang/slang-check-conformance.cpp
index 81d36c6f6..ce41df521 100644
--- a/source/slang/slang-check-conformance.cpp
+++ b/source/slang/slang-check-conformance.cpp
@@ -173,11 +173,11 @@ namespace Slang
if( auto aggTypeDeclRef = declRef.as<AggTypeDecl>() )
{
- checkDecl(aggTypeDeclRef.getDecl());
+ ensureDecl(aggTypeDeclRef, DeclCheckState::CanEnumerateBases);
for( auto inheritanceDeclRef : getMembersOfTypeWithExt<InheritanceDecl>(aggTypeDeclRef))
{
- checkDecl(inheritanceDeclRef.getDecl());
+ ensureDecl(inheritanceDeclRef, DeclCheckState::CanUseBaseOfInheritanceDecl);
// Here we will recursively look up conformance on the type
// that is being inherited from. This is dangerous because
@@ -210,7 +210,7 @@ namespace Slang
// if an inheritance decl is not found, try to find a GenericTypeConstraintDecl
for (auto genConstraintDeclRef : getMembersOfType<GenericTypeConstraintDecl>(aggTypeDeclRef))
{
- checkDecl(genConstraintDeclRef.getDecl());
+ ensureDecl(genConstraintDeclRef, DeclCheckState::CanUseBaseOfInheritanceDecl);
auto inheritedType = GetSup(genConstraintDeclRef);
TypeWitnessBreadcrumb breadcrumb;
breadcrumb.prev = inBreadcrumbs;
diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp
index 581cefdcb..879f1c5fa 100644
--- a/source/slang/slang-check-decl.cpp
+++ b/source/slang/slang-check-decl.cpp
@@ -14,6 +14,114 @@
namespace Slang
{
+ /// Visitor to transition declarations to `DeclCheckState::CheckedModifiers`
+ struct SemanticsDeclModifiersVisitor
+ : public SemanticsDeclVisitorBase
+ , public DeclVisitor<SemanticsDeclModifiersVisitor>
+ {
+ SemanticsDeclModifiersVisitor(SharedSemanticsContext* shared)
+ : SemanticsDeclVisitorBase(shared)
+ {}
+
+ void visitDeclGroup(DeclGroup*) {}
+
+ void visitDecl(Decl* decl)
+ {
+ checkModifiers(decl);
+ }
+ };
+
+ struct SemanticsDeclHeaderVisitor
+ : public SemanticsDeclVisitorBase
+ , public DeclVisitor<SemanticsDeclHeaderVisitor>
+ {
+ SemanticsDeclHeaderVisitor(SharedSemanticsContext* shared)
+ : SemanticsDeclVisitorBase(shared)
+ {}
+
+ void visitDecl(Decl*) {}
+ void visitDeclGroup(DeclGroup*) {}
+
+ void checkVarDeclCommon(RefPtr<VarDeclBase> varDecl);
+
+ void visitVarDecl(VarDecl* varDecl)
+ {
+ checkVarDeclCommon(varDecl);
+ }
+
+ void visitImportDecl(ImportDecl* decl);
+
+ void visitGenericTypeParamDecl(GenericTypeParamDecl* decl);
+
+ void visitGenericValueParamDecl(GenericValueParamDecl* decl);
+
+ void visitGenericTypeConstraintDecl(GenericTypeConstraintDecl* decl);
+
+ void visitGenericDecl(GenericDecl* genericDecl);
+
+ void visitTypeDefDecl(TypeDefDecl* decl);
+
+ void visitGlobalGenericParamDecl(GlobalGenericParamDecl* decl);
+
+ void visitAssocTypeDecl(AssocTypeDecl* decl);
+
+ void visitFuncDecl(FuncDecl* funcDecl);
+
+ void visitParamDecl(ParamDecl* paramDecl);
+
+ void visitConstructorDecl(ConstructorDecl* decl);
+
+ void visitSubscriptDecl(SubscriptDecl* decl);
+
+ void visitAccessorDecl(AccessorDecl* decl);
+ };
+
+ struct SemanticsDeclBasesVisitor
+ : public SemanticsDeclVisitorBase
+ , public DeclVisitor<SemanticsDeclBasesVisitor>
+ {
+ SemanticsDeclBasesVisitor(SharedSemanticsContext* shared)
+ : SemanticsDeclVisitorBase(shared)
+ {}
+
+ void visitDecl(Decl*) {}
+ void visitDeclGroup(DeclGroup*) {}
+
+ void visitInheritanceDecl(InheritanceDecl* inheritanceDecl);
+
+ void visitAggTypeDecl(AggTypeDecl* decl);
+
+ void visitEnumDecl(EnumDecl* decl);
+
+ void visitExtensionDecl(ExtensionDecl* decl);
+ };
+
+ struct SemanticsDeclBodyVisitor
+ : public SemanticsDeclVisitorBase
+ , public DeclVisitor<SemanticsDeclBodyVisitor>
+ {
+ SemanticsDeclBodyVisitor(SharedSemanticsContext* shared)
+ : SemanticsDeclVisitorBase(shared)
+ {}
+
+ void visitDecl(Decl*) {}
+ void visitDeclGroup(DeclGroup*) {}
+
+ void checkVarDeclCommon(RefPtr<VarDeclBase> varDecl);
+
+ void visitVarDecl(VarDecl* varDecl)
+ {
+ checkVarDeclCommon(varDecl);
+ }
+
+ void visitEnumCaseDecl(EnumCaseDecl* decl);
+
+ void visitEnumDecl(EnumDecl* decl);
+
+ void visitFuncDecl(FuncDecl* funcDecl);
+
+ void visitParamDecl(ParamDecl* paramDecl);
+ };
/// Should the given `decl` nested in `parentDecl` be treated as a static rather than instance declaration?
bool isEffectivelyStatic(
@@ -104,6 +212,22 @@ namespace Slang
return true;
}
+ static bool _isLocalVar(VarDeclBase* varDecl)
+ {
+ auto pp = varDecl->ParentDecl;
+
+ if(as<ScopeDecl>(pp))
+ return true;
+
+ if(auto genericDecl = as<GenericDecl>(pp))
+ pp = genericDecl;
+
+ if(as<FuncDecl>(pp))
+ return true;
+
+ return false;
+ }
+
// Get the type to use when referencing a declaration
QualType getTypeForDeclRef(
Session* session,
@@ -114,7 +238,40 @@ namespace Slang
{
if( sema )
{
- sema->checkDecl(declRef.getDecl());
+ // Hack: if we are somehow referencing a local variable declaration
+ // before the line of code that defines it, then we need to diagnose
+ // an error.
+ //
+ // TODO: The right answer is that lookup should have been performed in
+ // the scope that was in place *before* the variable was declared, but
+ // this is a quick fix that at least alerts the user to how we are
+ // interpreting their code.
+ //
+ // We detect the problematic case by looking for an attempt to reference
+ // a local variable declaration when it is unchecked, or in the process
+ // of being checked (the latter case catches a local variable that refers
+ // to itself in its initial-value expression).
+ //
+ auto checkStateExt = declRef.getDecl()->checkState;
+ if( checkStateExt.getState() == DeclCheckState::Unchecked
+ || checkStateExt.isBeingChecked() )
+ {
+ if(auto varDecl = as<VarDecl>(declRef.getDecl()))
+ {
+ if(_isLocalVar(varDecl))
+ {
+ sema->getSink()->diagnose(varDecl, Diagnostics::localVariableUsedBeforeDeclared, varDecl);
+ return QualType(session->getErrorType());
+ }
+ }
+ }
+
+ // Once we've rules out the case of referencing a local declaration
+ // before it has been checked, we will go ahead and ensure that
+ // semantic checking has been performed on the chosen declaration,
+ // at least up to the point where we can query its type.
+ //
+ sema->ensureDecl(declRef, DeclCheckState::CanUseTypeOfValueDecl);
}
// We need to insert an appropriate type for the expression, based on
@@ -313,9 +470,9 @@ namespace Slang
return subst;
}
- void checkDecl(SemanticsVisitor* visitor, Decl* decl)
+ void ensureDecl(SemanticsVisitor* visitor, Decl* decl, DeclCheckState state)
{
- visitor->checkDecl(decl);
+ visitor->ensureDecl(decl, state);
}
bool SemanticsVisitor::isDeclUsableAsStaticMember(
@@ -381,13 +538,28 @@ namespace Slang
return isDeclUsableAsStaticMember(decl);
}
+ /// Dispatch an appropriate visitor to check `decl` up to state `state`
+ ///
+ /// The current state of `decl` must be `state-1`.
+ /// This call does *not* handle updating the state of `decl`; the
+ /// caller takes responsibility for doing so.
+ ///
+ static void _dispatchDeclCheckingVisitor(Decl* decl, DeclCheckState state, SharedSemanticsContext* shared);
+
// Make sure a declaration has been checked, so we can refer to it.
// Note that this may lead to us recursively invoking checking,
// so this may not be the best way to handle things.
- void SemanticsVisitor::EnsureDecl(RefPtr<Decl> decl, DeclCheckState state)
+ void SemanticsVisitor::ensureDecl(Decl* decl, DeclCheckState state)
{
+ // If the `decl` has already been checked up to or beyond `state`
+ // then there is nothing for us to do.
+ //
if (decl->IsChecked(state)) return;
- if (decl->checkState == DeclCheckState::CheckingHeader)
+
+ // Is the declaration already being checked, somewhere up the
+ // call stack from us?
+ //
+ if(decl->checkState.isBeingChecked())
{
// We tried to reference the same declaration while checking it!
//
@@ -399,58 +571,130 @@ namespace Slang
return;
}
- // Hack: if we are somehow referencing a local variable declaration
- // before the line of code that defines it, then we need to diagnose
- // an error.
+ // Set the flag that indicates we are checking this declaration,
+ // so that the cycle check above will catch us before we go
+ // into any infinite loops.
+ //
+ decl->checkState.setIsBeingChecked(true);
+
+ // Our task is to bring the `decl` up to `state` which may be
+ // one or more steps ahead of where it currently is. We can
+ // invoke a visitor designed to bring a declaration from state
+ // N to state N+1, and in general we might need multiple such
+ // passes to get `decl` to where we need it.
//
- // TODO: The right answer is that lookup should have been performed in
- // the scope that was in place *before* the variable was declared, but
- // this is a quick fix that at least alerts the user to how we are
- // interpreting their code.
+ // The coding of this loop is somewhat defensive to deal
+ // with special cases that will be described along the way.
//
- if (auto varDecl = as<VarDecl>(decl))
+ for(;;)
{
- if (auto parenScope = as<ScopeDecl>(varDecl->ParentDecl))
+ // The first thing is to check what state the decl is
+ // currently in at the start of this loop iteration,
+ // and to bail out if it has been checked up to
+ // (or beyond) our target state.
+ //
+ auto currentState = decl->checkState.getState();
+ if(currentState >= state)
+ break;
+
+ // Because our visitors are only designed to go from state
+ // N to N+1 in general, we will aspire to transition to
+ // a state that is one greater than `currentState`.
+ //
+ auto nextState = DeclCheckState(Int(currentState) + 1);
+
+ // We now dispatch an appropriate visitor based on `nextState`.
+ //
+ _dispatchDeclCheckingVisitor(decl, nextState, getShared());
+
+ // In the common case, the visitor will have done the necessary
+ // checking, but will *not* have updated the `checkState` on
+ // `decl`. In that case we will do the update here, to save
+ // us the complication of having to deal with state update in
+ // every single visitor method.
+ //
+ // However, sometimes a visitor *will* want to manually update
+ // the state of a declaration, and it may actually update it
+ // *past* the `nextState` we asked for (or even past the
+ // eventual target `state`). In those cases we don't want to
+ // accidentally set the state of `decl` to something lower
+ // than what has actually been checked, so we test for
+ // such cases here.
+ //
+ if(nextState > decl->checkState.getState())
{
- // TODO: This diagnostic should be emitted on the line that is referencing
- // the declaration. That requires `EnsureDecl` to take the requesting
- // location as a parameter.
- getSink()->diagnose(decl, Diagnostics::localVariableUsedBeforeDeclared, decl);
- return;
+ decl->SetCheckState(nextState);
}
}
- if (DeclCheckState::CheckingHeader > decl->checkState)
- {
- decl->SetCheckState(DeclCheckState::CheckingHeader);
- }
+ // Once we are done here, the state of `decl` should have
+ // been upgraded to (at least) `state`.
+ //
+ SLANG_ASSERT(decl->IsChecked(state));
- // Check the modifiers on the declaration first, in case
- // semantics of the body itself will depend on them.
- checkModifiers(decl);
+ // Now that we are done checking `decl` we need to restore
+ // its "is being checked" flag so that we don't generate
+ // errors the next time somebody calls `ensureDecl()` on it.
+ //
+ decl->checkState.setIsBeingChecked(false);
+ }
+
+ /// Recursively ensure the tree of declarations under `decl` is in `state`.
+ ///
+ /// This function does *not* handle declarations nested in function bodies
+ /// because those cannot be meaningfully checked outside of the context
+ /// of their surrounding statement(s).
+ ///
+ static void _ensureAllDeclsRec(
+ SemanticsDeclVisitorBase* visitor,
+ Decl* decl,
+ DeclCheckState state)
+ {
+ // Ensure `decl` itself first.
+ visitor->ensureDecl(decl, state);
+
+ // If `decl` is a container, then we want to ensure its children.
+ if(auto containerDecl = as<ContainerDecl>(decl))
+ {
+ // As an exception, if any of the child is a `ScopeDecl`,
+ // then that indicates that it represents a scope for local
+ // declarations under a statement (e.g., in a function body),
+ // and we don't want to check such local declarations here.
+ //
+ for(auto childDecl : containerDecl->Members)
+ {
+ if(as<ScopeDecl>(childDecl))
+ continue;
- // Use visitor pattern to dispatch to correct case
- dispatchDecl(decl);
+ _ensureAllDeclsRec(visitor, childDecl, state);
+ }
+ }
- if(state > decl->checkState)
+ // Note: the "inner" declaration of a `GenericDecl` is currently
+ // not exposed as one of its children (despite a `GenericDecl`
+ // being a `ContainerDecl`), so we need to handle the inner
+ // declaration of a generic as another case here.
+ //
+ if(auto genericDecl = as<GenericDecl>(decl))
{
- decl->SetCheckState(state);
+ _ensureAllDeclsRec(visitor, genericDecl->inner, state);
}
}
- void SemanticsVisitor::EnusreAllDeclsRec(RefPtr<Decl> decl)
+ static bool isUnsizedArrayType(Type* type)
{
- checkDecl(decl);
- if (auto containerDecl = as<ContainerDecl>(decl))
- {
- for (auto m : containerDecl->Members)
- {
- EnusreAllDeclsRec(m);
- }
- }
+ // Not an array?
+ auto arrayType = as<ArrayExpressionType>(type);
+ if (!arrayType) return false;
+
+ // Explicit element count given?
+ auto elementCount = arrayType->ArrayLength;
+ if (elementCount) return true;
+
+ return true;
}
- void SemanticsVisitor::CheckVarDeclCommon(RefPtr<VarDeclBase> varDecl)
+ void SemanticsDeclHeaderVisitor::checkVarDeclCommon(RefPtr<VarDeclBase> varDecl)
{
// A variable that didn't have an explicit type written must
// have its type inferred from the initial-value expression.
@@ -481,21 +725,29 @@ namespace Slang
varDecl->type.type = initExpr->type;
}
+ // If we've gone down this path, then the variable
+ // declaration is actually pretty far along in checking
varDecl->SetCheckState(DeclCheckState::Checked);
}
else
{
- if (function || checkingPhase == CheckingPhase::Header)
+ // A variable with an explicit type is simpler, for the
+ // most part.
+
+ TypeExp typeExp = CheckUsableType(varDecl->type);
+ varDecl->type = typeExp;
+ if (varDecl->type.Equals(getSession()->getVoidType()))
{
- TypeExp typeExp = CheckUsableType(varDecl->type);
- varDecl->type = typeExp;
- if (varDecl->type.Equals(getSession()->getVoidType()))
- {
- getSink()->diagnose(varDecl, Diagnostics::invalidTypeVoid);
- }
+ getSink()->diagnose(varDecl, Diagnostics::invalidTypeVoid);
}
- if (checkingPhase == CheckingPhase::Body)
+ // If this is an unsized array variable, then we first want to give
+ // it a chance to infer an array size from its initializer
+ //
+ // TODO(tfoley): May need to extend this to handle the
+ // multi-dimensional case...
+ //
+ if(isUnsizedArrayType(varDecl->type))
{
if (auto initExpr = varDecl->initExpr)
{
@@ -503,25 +755,29 @@ namespace Slang
initExpr = coerce(varDecl->type.Ptr(), initExpr);
varDecl->initExpr = initExpr;
- // If this is an array variable, then we first want to give
- // it a chance to infer an array size from its initializer
- //
- // TODO(tfoley): May need to extend this to handle the
- // multi-dimensional case...
- //
maybeInferArraySizeForVariable(varDecl);
- //
- // Next we want to make sure that the declared (or inferred)
- // size for the array meets whatever language-specific
- // constraints we want to enforce (e.g., disallow empty
- // arrays in specific cases)
- //
- validateArraySizeForVariable(varDecl);
+
+ varDecl->SetCheckState(DeclCheckState::Checked);
}
}
+ //
+ // Next we want to make sure that the declared (or inferred)
+ // size for the array meets whatever language-specific
+ // constraints we want to enforce (e.g., disallow empty
+ // arrays in specific cases)
+ //
+ validateArraySizeForVariable(varDecl);
+ }
+ }
+ void SemanticsDeclBodyVisitor::checkVarDeclCommon(RefPtr<VarDeclBase> varDecl)
+ {
+ if (auto initExpr = varDecl->initExpr)
+ {
+ initExpr = CheckTerm(initExpr);
+ initExpr = coerce(varDecl->type.Ptr(), initExpr);
+ varDecl->initExpr = initExpr;
}
- varDecl->SetCheckState(getCheckedState());
}
// Fill in default substitutions for the 'subtype' part of a type constraint decl
@@ -539,76 +795,55 @@ namespace Slang
}
}
- void SemanticsVisitor::CheckGenericConstraintDecl(GenericTypeConstraintDecl* decl)
+ void SemanticsDeclHeaderVisitor::visitGenericTypeConstraintDecl(GenericTypeConstraintDecl* decl)
{
// TODO: are there any other validations we can do at this point?
//
// There probably needs to be a kind of "occurs check" to make
// sure that the constraint actually applies to at least one
// of the parameters of the generic.
- if (decl->checkState == DeclCheckState::Unchecked)
- {
- decl->checkState = getCheckedState();
- CheckConstraintSubType(decl->sub);
- decl->sub = TranslateTypeNodeForced(decl->sub);
- decl->sup = TranslateTypeNodeForced(decl->sup);
- }
+ //
+ CheckConstraintSubType(decl->sub);
+ decl->sub = TranslateTypeNodeForced(decl->sub);
+ decl->sup = TranslateTypeNodeForced(decl->sup);
}
- void SemanticsVisitor::checkDecl(Decl* decl)
+ void SemanticsDeclHeaderVisitor::visitGenericTypeParamDecl(GenericTypeParamDecl* decl)
{
- EnsureDecl(decl, checkingPhase == CheckingPhase::Header ? DeclCheckState::CheckedHeader : DeclCheckState::Checked);
+ // TODO: could probably push checking the default value
+ // for a generic type parameter later.
+ //
+ decl->initType = CheckProperType(decl->initType);
}
- void SemanticsVisitor::checkGenericDeclHeader(GenericDecl* genericDecl)
+ void SemanticsDeclHeaderVisitor::visitGenericValueParamDecl(GenericValueParamDecl* decl)
{
- if (genericDecl->IsChecked(DeclCheckState::CheckedHeader))
- return;
- // check the parameters
+ checkVarDeclCommon(decl);
+ }
+
+ void SemanticsDeclHeaderVisitor::visitGenericDecl(GenericDecl* genericDecl)
+ {
+ genericDecl->SetCheckState(DeclCheckState::ReadyForLookup);
+
for (auto m : genericDecl->Members)
{
if (auto typeParam = as<GenericTypeParamDecl>(m))
{
- typeParam->initType = CheckProperType(typeParam->initType);
+ ensureDecl(typeParam, DeclCheckState::ReadyForReference);
}
else if (auto valParam = as<GenericValueParamDecl>(m))
{
- // TODO: some real checking here...
- CheckVarDeclCommon(valParam);
+ ensureDecl(valParam, DeclCheckState::ReadyForReference);
}
else if (auto constraint = as<GenericTypeConstraintDecl>(m))
{
- CheckGenericConstraintDecl(constraint);
+ ensureDecl(constraint, DeclCheckState::ReadyForReference);
}
}
-
- genericDecl->SetCheckState(DeclCheckState::CheckedHeader);
- }
-
- void SemanticsVisitor::visitGenericDecl(GenericDecl* genericDecl)
- {
- checkGenericDeclHeader(genericDecl);
-
- // check the nested declaration
- // TODO: this needs to be done in an appropriate environment...
- checkDecl(genericDecl->inner);
- genericDecl->SetCheckState(getCheckedState());
}
- void SemanticsVisitor::visitGenericTypeConstraintDecl(GenericTypeConstraintDecl * genericConstraintDecl)
+ void SemanticsDeclBasesVisitor::visitInheritanceDecl(InheritanceDecl* inheritanceDecl)
{
- if (genericConstraintDecl->IsChecked(DeclCheckState::CheckedHeader))
- return;
- // check the type being inherited from
- auto base = genericConstraintDecl->sup;
- base = TranslateTypeNode(base);
- genericConstraintDecl->sup = base;
- }
-
- void SemanticsVisitor::visitInheritanceDecl(InheritanceDecl* inheritanceDecl)
- {
- if (inheritanceDecl->IsChecked(DeclCheckState::CheckedHeader))
- return;
// check the type being inherited from
auto base = inheritanceDecl->base;
CheckConstraintSubType(base);
@@ -636,172 +871,182 @@ namespace Slang
getSink()->diagnose( base.exp, Diagnostics::expectedAnInterfaceGot, base.type);
}
- void SemanticsVisitor::visitSyntaxDecl(SyntaxDecl*)
- {
- // These are only used in the stdlib, so no checking is needed
- }
-
- void SemanticsVisitor::visitAttributeDecl(AttributeDecl*)
- {
- // These are only used in the stdlib, so no checking is needed
- }
-
- void SemanticsVisitor::visitGenericTypeParamDecl(GenericTypeParamDecl*)
+ // Concretize interface conformances so that we have witnesses as required for lookup.
+ // for lookup.
+ struct SemanticsDeclConformancesVisitor
+ : public SemanticsDeclVisitorBase
+ , public DeclVisitor<SemanticsDeclConformancesVisitor>
{
- // These are only used in the stdlib, so no checking is needed for now
- }
+ SemanticsDeclConformancesVisitor(SharedSemanticsContext* shared)
+ : SemanticsDeclVisitorBase(shared)
+ {}
- void SemanticsVisitor::visitGenericValueParamDecl(GenericValueParamDecl*)
- {
- // These are only used in the stdlib, so no checking is needed for now
- }
+ void visitDecl(Decl*) {}
+ void visitDeclGroup(DeclGroup*) {}
- void SemanticsVisitor::checkInterfaceConformancesRec(Decl* decl)
- {
// Any user-defined type may have declared interface conformances,
// which we should check.
//
- if( auto aggTypeDecl = as<AggTypeDecl>(decl) )
+ void visitAggTypeDecl(AggTypeDecl* aggTypeDecl)
{
checkAggTypeConformance(aggTypeDecl);
}
+
// Conformances can also come via `extension` declarations, and
// we should check them against the type(s) being extended.
//
- else if(auto extensionDecl = as<ExtensionDecl>(decl))
+ void visitExtensionDecl(ExtensionDecl* extensionDecl)
{
checkExtensionConformance(extensionDecl);
}
+ };
- // We need to handle the recursive cases here, the first
- // of which is a generic decl, where we want to recurivsely
- // check the inner declaration.
- //
- if(auto genericDecl = as<GenericDecl>(decl))
+ /// Recursively register any builtin declarations that need to be attached to the `session`.
+ ///
+ /// This function should only be needed for declarations in the standard library.
+ ///
+ static void _registerBuiltinDeclsRec(Session* session, Decl* decl)
+ {
+ if (auto builtinMod = decl->FindModifier<BuiltinTypeModifier>())
{
- checkInterfaceConformancesRec(genericDecl->inner);
+ registerBuiltinDecl(session, decl, builtinMod);
}
- // For any other kind of container declaration, we will
- // recurse into all of its member declarations, so that
- // we can handle, e.g., nested `struct` types.
- //
- else if(auto containerDecl = as<ContainerDecl>(decl))
+ if (auto magicMod = decl->FindModifier<MagicTypeModifier>())
{
- for(auto member : containerDecl->Members)
- {
- checkInterfaceConformancesRec(member);
- }
+ registerMagicDecl(session, decl, magicMod);
}
- }
- void SemanticsVisitor::visitModuleDecl(ModuleDecl* programNode)
- {
- // Try to register all the builtin decls
- for (auto decl : programNode->Members)
+ if(auto containerDecl = as<ContainerDecl>(decl))
{
- auto inner = decl;
- if (auto genericDecl = as<GenericDecl>(decl))
+ for(auto childDecl : containerDecl->Members)
{
- inner = genericDecl->inner;
- }
+ if(as<ScopeDecl>(childDecl))
+ continue;
- if (auto builtinMod = inner->FindModifier<BuiltinTypeModifier>())
- {
- registerBuiltinDecl(getSession(), decl, builtinMod);
- }
- if (auto magicMod = inner->FindModifier<MagicTypeModifier>())
- {
- registerMagicDecl(getSession(), decl, magicMod);
+ _registerBuiltinDeclsRec(session, childDecl);
}
}
-
- // We need/want to visit any `import` declarations before
- // anything else, to make sure that scoping works.
- for(auto& importDecl : programNode->getMembersOfType<ImportDecl>())
- {
- checkDecl(importDecl);
- }
- // register all extensions
- for (auto & s : programNode->getMembersOfType<ExtensionDecl>())
- registerExtension(s);
- for (auto & g : programNode->getMembersOfType<GenericDecl>())
+ if(auto genericDecl = as<GenericDecl>(decl))
{
- if (auto extDecl = as<ExtensionDecl>(g->inner))
- {
- checkGenericDeclHeader(g);
- registerExtension(extDecl);
- }
+ _registerBuiltinDeclsRec(session, genericDecl->inner);
}
- // check user defined attribute classes first
- for (auto decl : programNode->Members)
+ }
+
+ void SemanticsDeclVisitorBase::checkModule(ModuleDecl* moduleDecl)
+ {
+ // When we are dealing with code from the standard library,
+ // there is a potential problem where we might need to look
+ // up built-in types like `Int` through the session (e.g.,
+ // to determine the type for an integer literal), but those
+ // types might not have been registered yet. We solve that
+ // by doing a pre-process on standard-library code to find
+ // and register any built-in declarations.
+ //
+ // TODO: This could be factored into another visitor pass
+ // that fits the more standard checking below, but that would
+ // seemingly add overhead to checking things other than
+ // the standard library.
+ //
+ if(isFromStdLib(moduleDecl))
{
- if (auto typeMember = as<StructDecl>(decl))
- {
- bool isTypeAttributeClass = false;
- for (auto attrib : typeMember->GetModifiersOfType<UncheckedAttribute>())
- {
- if (attrib->name == getSession()->getNameObj("AttributeUsageAttribute"))
- {
- isTypeAttributeClass = true;
- break;
- }
- }
- if (isTypeAttributeClass)
- checkDecl(decl);
- }
+ _registerBuiltinDeclsRec(getSession(), moduleDecl);
}
- // check types
- for (auto & s : programNode->getMembersOfType<TypeDefDecl>())
- checkDecl(s.Ptr());
- for (int pass = 0; pass < 2; pass++)
+ // We need/want to visit any `import` declarations before
+ // anything else, to make sure that scoping works.
+ //
+ // TODO: This could be factored into another visitor pass
+ // that fits more with the standard checking below.
+ //
+ for(auto& importDecl : moduleDecl->getMembersOfType<ImportDecl>())
{
- checkingPhase = pass == 0 ? CheckingPhase::Header : CheckingPhase::Body;
-
- for (auto & s : programNode->getMembersOfType<AggTypeDecl>())
- {
- checkDecl(s.Ptr());
- }
- // HACK(tfoley): Visiting all generic declarations here,
- // because otherwise they won't get visited.
- for (auto & g : programNode->getMembersOfType<GenericDecl>())
- {
- checkDecl(g.Ptr());
- }
-
- // before checking conformance, make sure we check all the extension bodies
- // generic extension decls are already checked by the loop above
- for (auto & s : programNode->getMembersOfType<ExtensionDecl>())
- checkDecl(s);
-
- for (auto & func : programNode->getMembersOfType<FuncDecl>())
- {
- if (!func->IsChecked(getCheckedState()))
- {
- VisitFunctionDeclaration(func.Ptr());
- }
- }
- for (auto & func : programNode->getMembersOfType<FuncDecl>())
- {
- checkDecl(func);
- }
-
- if (getSink()->GetErrorCount() != 0)
- return;
-
- // Force everything to be fully checked, just in case
- // Note that we don't just call this on the program,
- // because we'd end up recursing into this very code path...
- for (auto d : programNode->Members)
- {
- EnusreAllDeclsRec(d);
- }
+ ensureDecl(importDecl, DeclCheckState::Checked);
+ }
- if (pass == 0)
- {
- checkInterfaceConformancesRec(programNode);
- }
+ // The entire goal of semantic checking is to get all of the
+ // declarations in the module up to `DeclCheckState::Checked`.
+ //
+ // The main catch is that checking one declaration A up to state M
+ // may required that declaration B is checked up to state N.
+ // A call to `ensureDecl(B, N)` can guarantee that things are checked
+ // when and where we need them, but that runs the risk of creating
+ // very deep recursion in the semantic checking.
+ //
+ // Instead, we would rather do more breadth-first checking,
+ // where everything gets checked up to state 1, 2, ...
+ // before anything gets too far ahead.
+ // We will therefore enumerate the states/phases for checking,
+ // and then iteratively try to update all declarations to each
+ // state in turn.
+ //
+ // Note: for a simpler language we could eliminate `ensureDecl`
+ // completely and *just* have these phases of checking.
+ // Unfortunately, we have some circularity between the phases:
+ //
+ // * Checking an overloaded call requires knowing the parameter
+ // types of all candidate callees.
+ //
+ // * Checking the parameter type of a function requires being
+ // able to check type expressions.
+ //
+ // * A type expression like `vector<T, N>` may have an arbitary
+ // expression for `N`.
+ //
+ // * An arbitrary expression may include function calls, which
+ // may be to overloaded functions.
+ //
+ // Languages like C++ solve the apparent problem by making
+ // restrictions on order of declaration/definition (and by
+ // requiring forward declarations or the `template`/`typename`
+ // keywrods in some cases).
+ //
+ // TODO: We could eventually eliminate the potential recursion
+ // in checking by splitting each phase into a "requirements gathering"
+ // step and an actual execution step.
+ //
+ // When checking a declaration D up to state S, the requirements
+ // gathering step would produce a list of pairs `(someDecl, someState)`
+ // indicating that `someDecl` must be in `someState` before the
+ // actual execution of checking for `(D,S)` can proceeed. The checker
+ // can then produce an elaborated dependency graph and select nodes
+ // for execution in an order that satisfies all the dependencies.
+ //
+ // Such a more elaborate checking scheme will have to wait for another
+ // day, but might be worth it (or even necessary) if/when we want to
+ // support incremental compilation.
+ //
+ DeclCheckState states[] =
+ {
+ DeclCheckState::ModifiersChecked,
+ DeclCheckState::ReadyForReference,
+ DeclCheckState::ReadyForLookup,
+ DeclCheckState::ReadyForLookup,
+ DeclCheckState::Checked
+ };
+ for(auto s : states)
+ {
+ // When advancing to state `s` we will recursively
+ // advance all declarations rooted in the module
+ // up to `s`.
+ //
+ // TODO: In cases where a large module is split across files,
+ // we could potentially parallelize front-end compilation by
+ // having multiple instances of the front end where each is
+ // only responsible for those declarations in a given file.
+ //
+ // Under that model, we might only apply later phases of
+ // checking (notably the final push to `DeclState::Checked`)
+ // to the subset of declarations coming from a given source
+ // file.
+ //
+ _ensureAllDeclsRec(this, moduleDecl, s);
}
+
+ // Once we have completed the above loop, all declarations not
+ // nested in function bodies should be in `DeclState::Checked`.
+ // Furthermore, because a fully checked function will have checked
+ // its body, this also means that all function bodies and the
+ // declarations they contain should be fully checked.
}
bool SemanticsVisitor::doesSignatureMatchRequirement(
@@ -1004,7 +1249,7 @@ namespace Slang
{
if(auto requiredTypeDeclRef = requiredMemberDeclRef.as<AssocTypeDecl>())
{
- checkDecl(subAggTypeDeclRef.getDecl());
+ ensureDecl(subAggTypeDeclRef, DeclCheckState::CanUseAsType);
auto satisfyingType = DeclRefType::Create(getSession(), subAggTypeDeclRef);
return doesTypeSatisfyAssociatedTypeRequirement(satisfyingType, requiredTypeDeclRef, witnessTable);
@@ -1016,7 +1261,7 @@ namespace Slang
// check if the specified type satisfies the constraints defined by the associated type
if (auto requiredTypeDeclRef = requiredMemberDeclRef.as<AssocTypeDecl>())
{
- checkDecl(typedefDeclRef.getDecl());
+ ensureDecl(typedefDeclRef, DeclCheckState::CanUseAsType);
auto satisfyingType = getNamedType(getSession(), typedefDeclRef);
return doesTypeSatisfyAssociatedTypeRequirement(satisfyingType, requiredTypeDeclRef, witnessTable);
@@ -1155,7 +1400,8 @@ namespace Slang
// We need to check the declaration of the interface
// before we can check that we conform to it.
- checkDecl(interfaceDeclRef.getDecl());
+ //
+ ensureDecl(interfaceDeclRef, DeclCheckState::CanReadInterfaceRequirements);
// We will construct the witness table, and register it
// *before* we go about checking fine-grained requirements,
@@ -1372,37 +1618,16 @@ namespace Slang
}
}
- void SemanticsVisitor::visitAggTypeDecl(AggTypeDecl* decl)
+ void SemanticsDeclBasesVisitor::visitAggTypeDecl(AggTypeDecl* decl)
{
- if (decl->IsChecked(getCheckedState()))
- return;
-
- // TODO: we should check inheritance declarations
- // first, since they need to be validated before
- // we can make use of the type (e.g., you need
- // to know that `A` inherits from `B` in order
- // to check an expression like `aValue.bMethod()`
- // where `aValue` is of type `A` but `bMethod`
- // is defined in type `B`.
- //
- // TODO: We should also add a pass that takes
- // all the stated inheritance relationships,
- // expands them to include implicit inheritance,
- // and then linearizes them. This would allow
- // later passes that need to know everything
- // a type inherits from to proceed linearly
- // through the list, rather than having to
- // recurse (and potentially see the same interface
- // more than once).
-
- decl->SetCheckState(DeclCheckState::CheckedHeader);
+ // TODO: We need to enumerate the bases here,
+ // and ideally form a "class precedence list"
+ // from them.
- // Now check all of the member declarations.
- for (auto member : decl->Members)
+ for( auto inheritanceDecl : decl->getMembersOfType<InheritanceDecl>() )
{
- checkDecl(member);
+ ensureDecl(inheritanceDecl, DeclCheckState::CanUseBaseOfInheritanceDecl);
}
- decl->SetCheckState(getCheckedState());
}
bool SemanticsVisitor::isIntegerBaseType(BaseType baseType)
@@ -1439,357 +1664,286 @@ namespace Slang
getSink()->diagnose(loc, Diagnostics::invalidEnumTagType, type);
}
- void SemanticsVisitor::visitEnumDecl(EnumDecl* decl)
+ void SemanticsDeclBasesVisitor::visitEnumDecl(EnumDecl* decl)
{
- if (decl->IsChecked(getCheckedState()))
- return;
-
- // We need to be careful to avoid recursion in the
- // type-checking logic. We will do the minimal work
- // to make the type usable in the first phase, and
- // then check the actual cases in the second phase.
- //
- if(this->checkingPhase == CheckingPhase::Header)
- {
- // Look at inheritance clauses, and
- // see if one of them is making the enum
- // "inherit" from a concrete type.
- // This will become the "tag" type
- // of the enum.
- RefPtr<Type> tagType;
- InheritanceDecl* tagTypeInheritanceDecl = nullptr;
- for(auto inheritanceDecl : decl->getMembersOfType<InheritanceDecl>())
- {
- checkDecl(inheritanceDecl);
+ // Look at inheritance clauses, and
+ // see if one of them is making the enum
+ // "inherit" from a concrete type.
+ // This will become the "tag" type
+ // of the enum.
+ RefPtr<Type> tagType;
+ InheritanceDecl* tagTypeInheritanceDecl = nullptr;
+ for(auto inheritanceDecl : decl->getMembersOfType<InheritanceDecl>())
+ {
+ ensureDecl(inheritanceDecl, DeclCheckState::CanUseBaseOfInheritanceDecl);
- // Look at the type being inherited from.
- auto superType = inheritanceDecl->base.type;
+ // Look at the type being inherited from.
+ auto superType = inheritanceDecl->base.type;
- if(auto errorType = as<ErrorType>(superType))
+ if(auto errorType = as<ErrorType>(superType))
+ {
+ // Ignore any erroneous inheritance clauses.
+ continue;
+ }
+ else if(auto declRefType = as<DeclRefType>(superType))
+ {
+ if(auto interfaceDeclRef = declRefType->declRef.as<InterfaceDecl>())
{
- // Ignore any erroneous inheritance clauses.
+ // Don't consider interface bases as candidates for
+ // the tag type.
continue;
}
- else if(auto declRefType = as<DeclRefType>(superType))
- {
- if(auto interfaceDeclRef = declRefType->declRef.as<InterfaceDecl>())
- {
- // Don't consider interface bases as candidates for
- // the tag type.
- continue;
- }
- }
-
- if(tagType)
- {
- // We already found a tag type.
- getSink()->diagnose(inheritanceDecl, Diagnostics::enumTypeAlreadyHasTagType);
- getSink()->diagnose(tagTypeInheritanceDecl, Diagnostics::seePreviousTagType);
- break;
- }
- else
- {
- tagType = superType;
- tagTypeInheritanceDecl = inheritanceDecl;
- }
}
- // If a tag type has not been set, then we
- // default it to the built-in `int` type.
- //
- // TODO: In the far-flung future we may want to distinguish
- // `enum` types that have a "raw representation" like this from
- // ones that are purely abstract and don't expose their
- // type of their tag.
- if(!tagType)
+ if(tagType)
{
- tagType = getSession()->getIntType();
+ // We already found a tag type.
+ getSink()->diagnose(inheritanceDecl, Diagnostics::enumTypeAlreadyHasTagType);
+ getSink()->diagnose(tagTypeInheritanceDecl, Diagnostics::seePreviousTagType);
+ break;
}
else
{
- // TODO: Need to establish that the tag
- // type is suitable. (e.g., if we are going
- // to allow raw values for case tags to be
- // derived automatically, then the tag
- // type needs to be some kind of integer type...)
- //
- // For now we will just be harsh and require it
- // to be one of a few builtin types.
- validateEnumTagType(tagType, tagTypeInheritanceDecl->loc);
+ tagType = superType;
+ tagTypeInheritanceDecl = inheritanceDecl;
}
- decl->tagType = tagType;
+ }
+ // If a tag type has not been set, then we
+ // default it to the built-in `int` type.
+ //
+ // TODO: In the far-flung future we may want to distinguish
+ // `enum` types that have a "raw representation" like this from
+ // ones that are purely abstract and don't expose their
+ // type of their tag.
+ if(!tagType)
+ {
+ tagType = getSession()->getIntType();
+ }
+ else
+ {
+ // TODO: Need to establish that the tag
+ // type is suitable. (e.g., if we are going
+ // to allow raw values for case tags to be
+ // derived automatically, then the tag
+ // type needs to be some kind of integer type...)
+ //
+ // For now we will just be harsh and require it
+ // to be one of a few builtin types.
+ validateEnumTagType(tagType, tagTypeInheritanceDecl->loc);
+ }
+ decl->tagType = tagType;
- // An `enum` type should automatically conform to the `__EnumType` interface.
- // The compiler needs to insert this conformance behind the scenes, and this
- // seems like the best place to do it.
- {
- // First, look up the type of the `__EnumType` interface.
- RefPtr<Type> enumTypeType = getSession()->getEnumTypeType();
-
- RefPtr<InheritanceDecl> enumConformanceDecl = new InheritanceDecl();
- enumConformanceDecl->ParentDecl = decl;
- enumConformanceDecl->loc = decl->loc;
- enumConformanceDecl->base.type = getSession()->getEnumTypeType();
- decl->Members.add(enumConformanceDecl);
-
- // The `__EnumType` interface has one required member, the `__Tag` type.
- // We need to satisfy this requirement automatically, rather than require
- // the user to actually declare a member with this name (otherwise we wouldn't
- // let them define a tag value with the name `__Tag`).
- //
- RefPtr<WitnessTable> witnessTable = new WitnessTable();
- enumConformanceDecl->witnessTable = witnessTable;
- Name* tagAssociatedTypeName = getSession()->getNameObj("__Tag");
- Decl* tagAssociatedTypeDecl = nullptr;
- if(auto enumTypeTypeDeclRefType = enumTypeType.dynamicCast<DeclRefType>())
+ // An `enum` type should automatically conform to the `__EnumType` interface.
+ // The compiler needs to insert this conformance behind the scenes, and this
+ // seems like the best place to do it.
+ {
+ // First, look up the type of the `__EnumType` interface.
+ RefPtr<Type> enumTypeType = getSession()->getEnumTypeType();
+
+ RefPtr<InheritanceDecl> enumConformanceDecl = new InheritanceDecl();
+ enumConformanceDecl->ParentDecl = decl;
+ enumConformanceDecl->loc = decl->loc;
+ enumConformanceDecl->base.type = getSession()->getEnumTypeType();
+ decl->Members.add(enumConformanceDecl);
+
+ // The `__EnumType` interface has one required member, the `__Tag` type.
+ // We need to satisfy this requirement automatically, rather than require
+ // the user to actually declare a member with this name (otherwise we wouldn't
+ // let them define a tag value with the name `__Tag`).
+ //
+ RefPtr<WitnessTable> witnessTable = new WitnessTable();
+ enumConformanceDecl->witnessTable = witnessTable;
+
+ Name* tagAssociatedTypeName = getSession()->getNameObj("__Tag");
+ Decl* tagAssociatedTypeDecl = nullptr;
+ if(auto enumTypeTypeDeclRefType = enumTypeType.dynamicCast<DeclRefType>())
+ {
+ if(auto enumTypeTypeInterfaceDecl = as<InterfaceDecl>(enumTypeTypeDeclRefType->declRef.getDecl()))
{
- if(auto enumTypeTypeInterfaceDecl = as<InterfaceDecl>(enumTypeTypeDeclRefType->declRef.getDecl()))
+ for(auto memberDecl : enumTypeTypeInterfaceDecl->Members)
{
- for(auto memberDecl : enumTypeTypeInterfaceDecl->Members)
+ if(memberDecl->getName() == tagAssociatedTypeName)
{
- if(memberDecl->getName() == tagAssociatedTypeName)
- {
- tagAssociatedTypeDecl = memberDecl;
- break;
- }
+ tagAssociatedTypeDecl = memberDecl;
+ break;
}
}
}
- if(!tagAssociatedTypeDecl)
- {
- SLANG_DIAGNOSE_UNEXPECTED(getSink(), decl, "failed to find built-in declaration '__Tag'");
- }
+ }
+ if(!tagAssociatedTypeDecl)
+ {
+ SLANG_DIAGNOSE_UNEXPECTED(getSink(), decl, "failed to find built-in declaration '__Tag'");
+ }
- // Okay, add the conformance witness for `__Tag` being satisfied by `tagType`
- witnessTable->requirementDictionary.Add(tagAssociatedTypeDecl, RequirementWitness(tagType));
+ // Okay, add the conformance witness for `__Tag` being satisfied by `tagType`
+ witnessTable->requirementDictionary.Add(tagAssociatedTypeDecl, RequirementWitness(tagType));
- // TODO: we actually also need to synthesize a witness for the conformance of `tagType`
- // to the `__BuiltinIntegerType` interface, because that is a constraint on the
- // associated type `__Tag`.
+ // TODO: we actually also need to synthesize a witness for the conformance of `tagType`
+ // to the `__BuiltinIntegerType` interface, because that is a constraint on the
+ // associated type `__Tag`.
- // TODO: eventually we should consider synthesizing other requirements for
- // the min/max tag values, or the total number of tags, so that people don't
- // have to declare these as additional cases.
+ // TODO: eventually we should consider synthesizing other requirements for
+ // the min/max tag values, or the total number of tags, so that people don't
+ // have to declare these as additional cases.
- enumConformanceDecl->SetCheckState(DeclCheckState::Checked);
- }
+ enumConformanceDecl->SetCheckState(DeclCheckState::Checked);
}
- else if( checkingPhase == CheckingPhase::Body )
- {
- auto enumType = DeclRefType::Create(
- getSession(),
- makeDeclRef(decl));
+ }
- auto tagType = decl->tagType;
+ void SemanticsDeclBodyVisitor::visitEnumDecl(EnumDecl* decl)
+ {
+ auto enumType = DeclRefType::Create(
+ getSession(),
+ makeDeclRef(decl));
- // Check the enum cases in order.
- for(auto caseDecl : decl->getMembersOfType<EnumCaseDecl>())
- {
- // Each case defines a value of the enum's type.
- //
- // TODO: If we ever support enum cases with payloads,
- // then they would probably have a type that is a
- // `FunctionType` from the payload types to the
- // enum type.
- //
- caseDecl->type.type = enumType;
+ auto tagType = decl->tagType;
- checkDecl(caseDecl);
- }
+ // Check the enum cases in order.
+ for(auto caseDecl : decl->getMembersOfType<EnumCaseDecl>())
+ {
+ // Each case defines a value of the enum's type.
+ //
+ // TODO: If we ever support enum cases with payloads,
+ // then they would probably have a type that is a
+ // `FunctionType` from the payload types to the
+ // enum type.
+ //
+ // TODO(tfoley): the case should grab its type when
+ // doing its own header checking, rather than rely on this...
+ caseDecl->type.type = enumType;
+
+ ensureDecl(caseDecl, DeclCheckState::Checked);
+ }
- // For any enum case that didn't provide an explicit
- // tag value, derived an appropriate tag value.
- IntegerLiteralValue defaultTag = 0;
- for(auto caseDecl : decl->getMembersOfType<EnumCaseDecl>())
+ // For any enum case that didn't provide an explicit
+ // tag value, derived an appropriate tag value.
+ IntegerLiteralValue defaultTag = 0;
+ for(auto caseDecl : decl->getMembersOfType<EnumCaseDecl>())
+ {
+ if(auto explicitTagValExpr = caseDecl->tagExpr)
{
- if(auto explicitTagValExpr = caseDecl->tagExpr)
- {
- // This tag has an initializer, so it should establish
- // the tag value for a successor case that doesn't
- // provide an explicit tag.
+ // This tag has an initializer, so it should establish
+ // the tag value for a successor case that doesn't
+ // provide an explicit tag.
- RefPtr<IntVal> explicitTagVal = TryConstantFoldExpr(explicitTagValExpr);
- if(explicitTagVal)
+ RefPtr<IntVal> explicitTagVal = TryConstantFoldExpr(explicitTagValExpr);
+ if(explicitTagVal)
+ {
+ if(auto constIntVal = as<ConstantIntVal>(explicitTagVal))
{
- if(auto constIntVal = as<ConstantIntVal>(explicitTagVal))
- {
- defaultTag = constIntVal->value;
- }
- else
- {
- // TODO: need to handle other possibilities here
- getSink()->diagnose(explicitTagValExpr, Diagnostics::unexpectedEnumTagExpr);
- }
+ defaultTag = constIntVal->value;
}
else
{
- // If this happens, then the explicit tag value expression
- // doesn't seem to be a constant after all. In this case
- // we expect the checking logic to have applied already.
+ // TODO: need to handle other possibilities here
+ getSink()->diagnose(explicitTagValExpr, Diagnostics::unexpectedEnumTagExpr);
}
}
else
{
- // This tag has no initializer, so it should use
- // the default tag value we are tracking.
- RefPtr<IntegerLiteralExpr> tagValExpr = new IntegerLiteralExpr();
- tagValExpr->loc = caseDecl->loc;
- tagValExpr->type = QualType(tagType);
- tagValExpr->value = defaultTag;
-
- caseDecl->tagExpr = tagValExpr;
+ // If this happens, then the explicit tag value expression
+ // doesn't seem to be a constant after all. In this case
+ // we expect the checking logic to have applied already.
}
-
- // Default tag for the next case will be one more than
- // for the most recent case.
- //
- // TODO: We might consider adding a `[flags]` attribute
- // that modifies this behavior to be `defaultTagForCase <<= 1`.
- //
- defaultTag++;
}
-
- // Now check any other member declarations.
- for(auto memberDecl : decl->Members)
+ else
{
- // Already checked inheritance declarations above.
- if(auto inheritanceDecl = as<InheritanceDecl>(memberDecl))
- continue;
-
- // Already checked enum case declarations above.
- if(auto caseDecl = as<EnumCaseDecl>(memberDecl))
- continue;
+ // This tag has no initializer, so it should use
+ // the default tag value we are tracking.
+ RefPtr<IntegerLiteralExpr> tagValExpr = new IntegerLiteralExpr();
+ tagValExpr->loc = caseDecl->loc;
+ tagValExpr->type = QualType(tagType);
+ tagValExpr->value = defaultTag;
- // TODO: Right now we don't support other kinds of
- // member declarations on an `enum`, but that is
- // something we may want to allow in the long run.
- //
- checkDecl(memberDecl);
+ caseDecl->tagExpr = tagValExpr;
}
+
+ // Default tag for the next case will be one more than
+ // for the most recent case.
+ //
+ // TODO: We might consider adding a `[flags]` attribute
+ // that modifies this behavior to be `defaultTagForCase <<= 1`.
+ //
+ defaultTag++;
}
- decl->SetCheckState(getCheckedState());
}
- void SemanticsVisitor::visitEnumCaseDecl(EnumCaseDecl* decl)
+ void SemanticsDeclBodyVisitor::visitEnumCaseDecl(EnumCaseDecl* decl)
{
- if (decl->IsChecked(getCheckedState()))
- return;
-
- if(checkingPhase == CheckingPhase::Body)
- {
- // An enum case had better appear inside an enum!
- //
- // TODO: Do we need/want to support generic cases some day?
- auto parentEnumDecl = as<EnumDecl>(decl->ParentDecl);
- SLANG_ASSERT(parentEnumDecl);
-
- // The tag type should have already been set by
- // the surrounding `enum` declaration.
- auto tagType = parentEnumDecl->tagType;
- SLANG_ASSERT(tagType);
-
- // Need to check the init expression, if present, since
- // that represents the explicit tag for this case.
- if(auto initExpr = decl->tagExpr)
- {
- initExpr = CheckExpr(initExpr);
- initExpr = coerce(tagType, initExpr);
+ // An enum case had better appear inside an enum!
+ //
+ // TODO: Do we need/want to support generic cases some day?
+ auto parentEnumDecl = as<EnumDecl>(decl->ParentDecl);
+ SLANG_ASSERT(parentEnumDecl);
- // We want to enforce that this is an integer constant
- // expression, but we don't actually care to retain
- // the value.
- CheckIntegerConstantExpression(initExpr);
+ // The tag type should have already been set by
+ // the surrounding `enum` declaration.
+ auto tagType = parentEnumDecl->tagType;
+ SLANG_ASSERT(tagType);
- decl->tagExpr = initExpr;
- }
- }
+ // Need to check the init expression, if present, since
+ // that represents the explicit tag for this case.
+ if(auto initExpr = decl->tagExpr)
+ {
+ initExpr = CheckExpr(initExpr);
+ initExpr = coerce(tagType, initExpr);
- decl->SetCheckState(getCheckedState());
- }
+ // We want to enforce that this is an integer constant
+ // expression, but we don't actually care to retain
+ // the value.
+ CheckIntegerConstantExpression(initExpr);
- void SemanticsVisitor::visitDeclGroup(DeclGroup* declGroup)
- {
- for (auto decl : declGroup->decls)
- {
- dispatchDecl(decl);
+ decl->tagExpr = initExpr;
}
}
- void SemanticsVisitor::visitTypeDefDecl(TypeDefDecl* decl)
+ void SemanticsVisitor::ensureDeclBase(DeclBase* declBase, DeclCheckState state)
{
- if (decl->IsChecked(getCheckedState())) return;
- if (checkingPhase == CheckingPhase::Header)
+ if(auto decl = as<Decl>(declBase))
{
- decl->type = CheckProperType(decl->type);
+ ensureDecl(decl, state);
}
- decl->SetCheckState(getCheckedState());
- }
-
- void SemanticsVisitor::visitGlobalGenericParamDecl(GlobalGenericParamDecl* decl)
- {
- if (decl->IsChecked(getCheckedState())) return;
- if (checkingPhase == CheckingPhase::Header)
+ else if(auto declGroup = as<DeclGroup>(declBase))
{
- decl->SetCheckState(DeclCheckState::CheckedHeader);
- // global generic param only allowed in global scope
- auto program = as<ModuleDecl>(decl->ParentDecl);
- if (!program)
- getSink()->diagnose(decl, Slang::Diagnostics::globalGenParamInGlobalScopeOnly);
- // Now check all of the member declarations.
- for (auto member : decl->Members)
+ for(auto dd : declGroup->decls)
{
- checkDecl(member);
+ ensureDecl(dd, state);
}
}
- decl->SetCheckState(getCheckedState());
+ else
+ {
+ SLANG_UNEXPECTED("unknown case for declaration");
+ }
}
- void SemanticsVisitor::visitAssocTypeDecl(AssocTypeDecl* decl)
+ void SemanticsDeclHeaderVisitor::visitTypeDefDecl(TypeDefDecl* decl)
{
- if (decl->IsChecked(getCheckedState())) return;
- if (checkingPhase == CheckingPhase::Header)
- {
- decl->SetCheckState(DeclCheckState::CheckedHeader);
-
- // assoctype only allowed in an interface
- auto interfaceDecl = as<InterfaceDecl>(decl->ParentDecl);
- if (!interfaceDecl)
- getSink()->diagnose(decl, Slang::Diagnostics::assocTypeInInterfaceOnly);
-
- // Now check all of the member declarations.
- for (auto member : decl->Members)
- {
- checkDecl(member);
- }
- }
- decl->SetCheckState(getCheckedState());
+ decl->type = CheckProperType(decl->type);
}
- void SemanticsVisitor::visitFuncDecl(FuncDecl* functionNode)
+ void SemanticsDeclHeaderVisitor::visitGlobalGenericParamDecl(GlobalGenericParamDecl* decl)
{
- if (functionNode->IsChecked(getCheckedState()))
- return;
+ // global generic param only allowed in global scope
+ auto program = as<ModuleDecl>(decl->ParentDecl);
+ if (!program)
+ getSink()->diagnose(decl, Slang::Diagnostics::globalGenParamInGlobalScopeOnly);
+ }
- if (checkingPhase == CheckingPhase::Header)
- {
- VisitFunctionDeclaration(functionNode);
- }
- // TODO: This should really only set "checked header"
- functionNode->SetCheckState(getCheckedState());
+ void SemanticsDeclHeaderVisitor::visitAssocTypeDecl(AssocTypeDecl* decl)
+ {
+ // assoctype only allowed in an interface
+ auto interfaceDecl = as<InterfaceDecl>(decl->ParentDecl);
+ if (!interfaceDecl)
+ getSink()->diagnose(decl, Slang::Diagnostics::assocTypeInInterfaceOnly);
+ }
- if (checkingPhase == CheckingPhase::Body)
+ void SemanticsDeclBodyVisitor::visitFuncDecl(FuncDecl* funcDecl)
+ {
+ if (auto body = funcDecl->Body)
{
- // TODO: should put the checking of the body onto a "work list"
- // to avoid recursion here.
- if (functionNode->Body)
- {
- auto oldFunc = function;
- this->function = functionNode;
- checkStmt(functionNode->Body);
- this->function = oldFunc;
- }
+ checkBodyStmt(body, funcDecl);
}
}
@@ -2189,12 +2343,7 @@ namespace Slang
}
}
- void SemanticsVisitor::visitScopeDecl(ScopeDecl*)
- {
- // Nothing to do
- }
-
- void SemanticsVisitor::visitParamDecl(ParamDecl* paramDecl)
+ void SemanticsDeclHeaderVisitor::visitParamDecl(ParamDecl* paramDecl)
{
// TODO: This logic should be shared with the other cases of
// variable declarations. The main reason I am not doing it
@@ -2208,8 +2357,11 @@ namespace Slang
typeExpr = CheckUsableType(typeExpr);
paramDecl->type = typeExpr;
}
+ }
- paramDecl->SetCheckState(DeclCheckState::CheckedHeader);
+ void SemanticsDeclBodyVisitor::visitParamDecl(ParamDecl* paramDecl)
+ {
+ auto typeExpr = paramDecl->type;
// The "initializer" expression for a parameter represents
// a default argument value to use if an explicit one is
@@ -2245,33 +2397,26 @@ namespace Slang
getSink()->diagnose(initExpr, Diagnostics::outputParameterCannotHaveDefaultValue);
}
}
-
- paramDecl->SetCheckState(DeclCheckState::Checked);
}
- void SemanticsVisitor::VisitFunctionDeclaration(FuncDecl *functionNode)
+ void SemanticsDeclHeaderVisitor::visitFuncDecl(FuncDecl* funcDecl)
{
- if (functionNode->IsChecked(DeclCheckState::CheckedHeader)) return;
- functionNode->SetCheckState(DeclCheckState::CheckingHeader);
- auto oldFunc = this->function;
- this->function = functionNode;
-
- auto resultType = functionNode->ReturnType;
+ auto resultType = funcDecl->ReturnType;
if(resultType.exp)
{
- resultType = CheckProperType(functionNode->ReturnType);
+ resultType = CheckProperType(resultType);
}
else
{
resultType = TypeExp(getSession()->getVoidType());
}
- functionNode->ReturnType = resultType;
+ funcDecl->ReturnType = resultType;
HashSet<Name*> paraNames;
- for (auto & para : functionNode->GetParameters())
+ for (auto & para : funcDecl->GetParameters())
{
- EnsureDecl(para, DeclCheckState::CheckedHeader);
+ ensureDecl(para, DeclCheckState::ReadyForReference);
if (paraNames.Contains(para->getName()))
{
@@ -2280,11 +2425,9 @@ namespace Slang
else
paraNames.Add(para->getName());
}
- this->function = oldFunc;
- functionNode->SetCheckState(DeclCheckState::CheckedHeader);
// One last bit of validation: check if we are redeclaring an existing function
- ValidateFunctionRedeclaration(functionNode);
+ ValidateFunctionRedeclaration(funcDecl);
}
IntegerLiteralValue SemanticsVisitor::GetMinBound(RefPtr<IntVal> val)
@@ -2353,21 +2496,9 @@ namespace Slang
}
}
- void SemanticsVisitor::visitVarDecl(VarDecl* varDecl)
- {
- CheckVarDeclCommon(varDecl);
- }
-
- void SemanticsVisitor::registerExtension(ExtensionDecl* decl)
+ void SemanticsDeclBasesVisitor::visitExtensionDecl(ExtensionDecl* decl)
{
- if (decl->IsChecked(DeclCheckState::CheckedHeader))
- return;
-
- decl->SetCheckState(DeclCheckState::CheckingHeader);
decl->targetType = CheckProperType(decl->targetType);
- decl->SetCheckState(DeclCheckState::CheckedHeader);
-
- // TODO: need to check that the target type names a declaration...
if (auto targetDeclRefType = as<DeclRefType>(decl->targetType))
{
@@ -2383,22 +2514,6 @@ namespace Slang
getSink()->diagnose(decl->targetType.exp, Diagnostics::unimplemented, "expected a nominal type here");
}
- void SemanticsVisitor::visitExtensionDecl(ExtensionDecl* decl)
- {
- if (decl->IsChecked(getCheckedState())) return;
-
- if (!as<DeclRefType>(decl->targetType))
- {
- getSink()->diagnose(decl->targetType.exp, Diagnostics::unimplemented, "expected a nominal type here");
- }
- // now check the members of the extension
- for (auto m : decl->Members)
- {
- checkDecl(m);
- }
- decl->SetCheckState(getCheckedState());
- }
-
RefPtr<Type> SemanticsVisitor::findResultTypeForConstructorDecl(ConstructorDecl* decl)
{
// We want to look at the parent of the declaration,
@@ -2436,35 +2551,23 @@ namespace Slang
}
}
- void SemanticsVisitor::visitConstructorDecl(ConstructorDecl* decl)
+ void SemanticsDeclHeaderVisitor::visitConstructorDecl(ConstructorDecl* decl)
{
- if (decl->IsChecked(getCheckedState())) return;
- if (checkingPhase == CheckingPhase::Header)
- {
- decl->SetCheckState(DeclCheckState::CheckingHeader);
-
- for (auto& paramDecl : decl->GetParameters())
- {
- paramDecl->type = CheckUsableType(paramDecl->type);
- }
-
- // We need to compute the result tyep for this declaration,
- // since it wasn't filled in for us.
- decl->ReturnType.type = findResultTypeForConstructorDecl(decl);
- }
- else
+ for (auto& paramDecl : decl->GetParameters())
{
- // TODO(tfoley): check body
+ ensureDecl(paramDecl, DeclCheckState::CanUseTypeOfValueDecl);
}
- decl->SetCheckState(getCheckedState());
+
+ // We need to compute the result tyep for this declaration,
+ // since it wasn't filled in for us.
+ decl->ReturnType.type = findResultTypeForConstructorDecl(decl);
}
- void SemanticsVisitor::visitSubscriptDecl(SubscriptDecl* decl)
+ void SemanticsDeclHeaderVisitor::visitSubscriptDecl(SubscriptDecl* decl)
{
- if (decl->IsChecked(getCheckedState())) return;
for (auto& paramDecl : decl->GetParameters())
{
- paramDecl->type = CheckUsableType(paramDecl->type);
+ ensureDecl(paramDecl, DeclCheckState::CanUseTypeOfValueDecl);
}
decl->ReturnType = CheckUsableType(decl->ReturnType);
@@ -2494,40 +2597,25 @@ namespace Slang
getterDecl->ParentDecl = decl;
decl->Members.add(getterDecl);
}
-
- for(auto mm : decl->Members)
- {
- checkDecl(mm);
- }
-
- decl->SetCheckState(getCheckedState());
}
- void SemanticsVisitor::visitAccessorDecl(AccessorDecl* decl)
+ void SemanticsDeclHeaderVisitor::visitAccessorDecl(AccessorDecl* decl)
{
- if (checkingPhase == CheckingPhase::Header)
+ // An accessor must appear nested inside a subscript declaration (today),
+ // or a property declaration (when we add them). It will derive
+ // its return type from the outer declaration, so we handle both
+ // of these checks at the same place.
+ auto parent = decl->ParentDecl;
+ if (auto parentSubscript = as<SubscriptDecl>(parent))
{
- // An accessor must appear nested inside a subscript declaration (today),
- // or a property declaration (when we add them). It will derive
- // its return type from the outer declaration, so we handle both
- // of these checks at the same place.
- auto parent = decl->ParentDecl;
- if (auto parentSubscript = as<SubscriptDecl>(parent))
- {
- decl->ReturnType = parentSubscript->ReturnType;
- }
- // TODO: when we add "property" declarations, check for them here
- else
- {
- getSink()->diagnose(decl, Diagnostics::accessorMustBeInsideSubscriptOrProperty);
- }
-
+ ensureDecl(parentSubscript, DeclCheckState::CanUseTypeOfValueDecl);
+ decl->ReturnType = parentSubscript->ReturnType;
}
+ // TODO: when we add "property" declarations, check for them here
else
{
- // TODO: check the body!
+ getSink()->diagnose(decl, Diagnostics::accessorMustBeInsideSubscriptOrProperty);
}
- decl->SetCheckState(getCheckedState());
}
GenericDecl* SemanticsVisitor::GetOuterGeneric(Decl* decl)
@@ -2655,6 +2743,7 @@ namespace Slang
QualType SemanticsVisitor::GetTypeForDeclRef(DeclRef<Decl> declRef)
{
+ RefPtr<Type> typeResult;
return getTypeForDeclRef(
getSession(),
this,
@@ -2667,6 +2756,7 @@ namespace Slang
{
// If we've imported this one already, then
// skip the step where we modify the current scope.
+ auto& importedModules = getShared()->importedModules;
if (importedModules.Contains(moduleDecl))
{
return;
@@ -2693,16 +2783,8 @@ namespace Slang
}
}
- void SemanticsVisitor::visitEmptyDecl(EmptyDecl* /*decl*/)
- {
- // nothing to do
- }
-
- void SemanticsVisitor::visitImportDecl(ImportDecl* decl)
+ void SemanticsDeclHeaderVisitor::visitImportDecl(ImportDecl* decl)
{
- if(decl->IsChecked(DeclCheckState::CheckedHeader))
- return;
-
// We need to look for a module with the specified name
// (whether it has already been loaded, or needs to
// be loaded), and then put its declarations into
@@ -2738,8 +2820,32 @@ namespace Slang
{
module->addModuleDependency(importedModule);
}
+ }
+
+ static void _dispatchDeclCheckingVisitor(Decl* decl, DeclCheckState state, SharedSemanticsContext* shared)
+ {
+ switch(state)
+ {
+ case DeclCheckState::ModifiersChecked:
+ SemanticsDeclModifiersVisitor(shared).dispatch(decl);
+ break;
+
+ case DeclCheckState::ReadyForReference:
+ SemanticsDeclHeaderVisitor(shared).dispatch(decl);
+ break;
- decl->SetCheckState(getCheckedState());
+ case DeclCheckState::ReadyForLookup:
+ SemanticsDeclBasesVisitor(shared).dispatch(decl);
+ break;
+
+ case DeclCheckState::ReadyForConformances:
+ SemanticsDeclConformancesVisitor(shared).dispatch(decl);
+ break;
+
+ case DeclCheckState::Checked:
+ SemanticsDeclBodyVisitor(shared).dispatch(decl);
+ break;
+ }
}
}
diff --git a/source/slang/slang-check-expr.cpp b/source/slang/slang-check-expr.cpp
index b9400f34a..9aa081e1b 100644
--- a/source/slang/slang-check-expr.cpp
+++ b/source/slang/slang-check-expr.cpp
@@ -416,7 +416,9 @@ namespace Slang
RefPtr<Expr> SemanticsVisitor::CheckTerm(RefPtr<Expr> term)
{
if (!term) return nullptr;
- return ExprVisitor::dispatch(term);
+
+ SemanticsExprVisitor exprVisitor(getShared());
+ return exprVisitor.dispatch(term);
}
RefPtr<Expr> SemanticsVisitor::CreateErrorExpr(Expr* expr)
@@ -448,13 +450,13 @@ namespace Slang
return nullptr;
}
- RefPtr<Expr> SemanticsVisitor::visitBoolLiteralExpr(BoolLiteralExpr* expr)
+ RefPtr<Expr> SemanticsExprVisitor::visitBoolLiteralExpr(BoolLiteralExpr* expr)
{
expr->type = getSession()->getBoolType();
return expr;
}
- RefPtr<Expr> SemanticsVisitor::visitIntegerLiteralExpr(IntegerLiteralExpr* expr)
+ RefPtr<Expr> SemanticsExprVisitor::visitIntegerLiteralExpr(IntegerLiteralExpr* expr)
{
// The expression might already have a type, determined by its suffix.
// It it doesn't, we will give it a default type.
@@ -474,7 +476,7 @@ namespace Slang
return expr;
}
- RefPtr<Expr> SemanticsVisitor::visitFloatingPointLiteralExpr(FloatingPointLiteralExpr* expr)
+ RefPtr<Expr> SemanticsExprVisitor::visitFloatingPointLiteralExpr(FloatingPointLiteralExpr* expr)
{
if(!expr->type.type)
{
@@ -483,7 +485,7 @@ namespace Slang
return expr;
}
- RefPtr<Expr> SemanticsVisitor::visitStringLiteralExpr(StringLiteralExpr* expr)
+ RefPtr<Expr> SemanticsExprVisitor::visitStringLiteralExpr(StringLiteralExpr* expr)
{
expr->type = getSession()->getStringType();
return expr;
@@ -766,7 +768,7 @@ namespace Slang
return subscriptExpr;
}
- RefPtr<Expr> SemanticsVisitor::visitIndexExpr(IndexExpr* subscriptExpr)
+ RefPtr<Expr> SemanticsExprVisitor::visitIndexExpr(IndexExpr* subscriptExpr)
{
auto baseExpr = subscriptExpr->BaseExpression;
baseExpr = CheckExpr(baseExpr);
@@ -804,7 +806,6 @@ namespace Slang
elementType,
elementCount);
- typeResult = arrayType;
subscriptExpr->type = QualType(getTypeType(arrayType));
return subscriptExpr;
}
@@ -873,7 +874,7 @@ namespace Slang
}
}
- RefPtr<Expr> SemanticsVisitor::visitParenExpr(ParenExpr* expr)
+ RefPtr<Expr> SemanticsExprVisitor::visitParenExpr(ParenExpr* expr)
{
auto base = expr->base;
base = CheckTerm(base);
@@ -920,7 +921,7 @@ namespace Slang
}
}
- RefPtr<Expr> SemanticsVisitor::visitAssignExpr(AssignExpr* expr)
+ RefPtr<Expr> SemanticsExprVisitor::visitAssignExpr(AssignExpr* expr)
{
expr->left = CheckExpr(expr->left);
@@ -1032,7 +1033,7 @@ namespace Slang
return rs;
}
- RefPtr<Expr> SemanticsVisitor::visitInvokeExpr(InvokeExpr *expr)
+ RefPtr<Expr> SemanticsExprVisitor::visitInvokeExpr(InvokeExpr *expr)
{
// check the base expression first
expr->FunctionExpr = CheckExpr(expr->FunctionExpr);
@@ -1045,7 +1046,7 @@ namespace Slang
return CheckInvokeExprWithCheckedOperands(expr);
}
- RefPtr<Expr> SemanticsVisitor::visitVarExpr(VarExpr *expr)
+ RefPtr<Expr> SemanticsExprVisitor::visitVarExpr(VarExpr *expr)
{
// If we've already resolved this expression, don't try again.
if (expr->declRef)
@@ -1068,7 +1069,7 @@ namespace Slang
return expr;
}
- RefPtr<Expr> SemanticsVisitor::visitTypeCastExpr(TypeCastExpr * expr)
+ RefPtr<Expr> SemanticsExprVisitor::visitTypeCastExpr(TypeCastExpr * expr)
{
// Check the term we are applying first
auto funcExpr = expr->FunctionExpr;
@@ -1421,7 +1422,7 @@ namespace Slang
return lookupMemberResultFailure(expr, baseType);
}
- RefPtr<Expr> SemanticsVisitor::visitStaticMemberExpr(StaticMemberExpr* expr)
+ RefPtr<Expr> SemanticsExprVisitor::visitStaticMemberExpr(StaticMemberExpr* expr)
{
expr->BaseExpression = CheckExpr(expr->BaseExpression);
@@ -1452,7 +1453,7 @@ namespace Slang
return expr;
}
- RefPtr<Expr> SemanticsVisitor::visitMemberExpr(MemberExpr * expr)
+ RefPtr<Expr> SemanticsExprVisitor::visitMemberExpr(MemberExpr * expr)
{
expr->BaseExpression = CheckExpr(expr->BaseExpression);
@@ -1521,7 +1522,7 @@ namespace Slang
}
}
- RefPtr<Expr> SemanticsVisitor::visitInitializerListExpr(InitializerListExpr* expr)
+ RefPtr<Expr> SemanticsExprVisitor::visitInitializerListExpr(InitializerListExpr* expr)
{
// When faced with an initializer list, we first just check the sub-expressions blindly.
// Actually making them conform to a desired type will wait for when we know the desired
@@ -1539,7 +1540,7 @@ namespace Slang
// Perform semantic checking of an object-oriented `this`
// expression.
- RefPtr<Expr> SemanticsVisitor::visitThisExpr(ThisExpr* expr)
+ RefPtr<Expr> SemanticsExprVisitor::visitThisExpr(ThisExpr* expr)
{
// A `this` expression will default to immutable.
expr->type.IsLeftValue = false;
@@ -1561,7 +1562,7 @@ namespace Slang
}
else if (auto aggTypeDecl = as<AggTypeDecl>(containerDecl))
{
- checkDecl(aggTypeDecl);
+ ensureDecl(aggTypeDecl, DeclCheckState::CanUseAsType);
// Okay, we are using `this` in the context of an
// aggregate type, so the expression should be
@@ -1573,7 +1574,7 @@ namespace Slang
}
else if (auto extensionDecl = as<ExtensionDecl>(containerDecl))
{
- checkDecl(extensionDecl);
+ ensureDecl(extensionDecl, DeclCheckState::CanUseExtensionTargetType);
// When `this` is used in the context of an `extension`
// declaration, then it should refer to an instance of
diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h
index 0cc9eb628..7027c4671 100644
--- a/source/slang/slang-check-impl.h
+++ b/source/slang/slang-check-impl.h
@@ -198,25 +198,9 @@ namespace Slang
Dictionary<BasicTypeKeyPair, ConversionCost> conversionCostCache;
};
- enum class CheckingPhase
+ /// Shared state for a semantics-checking session.
+ struct SharedSemanticsContext
{
- Header, Body
- };
-
- struct SemanticsVisitor
- : ExprVisitor<SemanticsVisitor, RefPtr<Expr>>
- , StmtVisitor<SemanticsVisitor>
- , DeclVisitor<SemanticsVisitor>
- {
- CheckingPhase checkingPhase = CheckingPhase::Header;
- DeclCheckState getCheckedState()
- {
- if (checkingPhase == CheckingPhase::Body)
- return DeclCheckState::Checked;
- else
- return DeclCheckState::CheckedHeader;
- }
-
Linkage* m_linkage = nullptr;
DiagnosticSink* m_sink = nullptr;
@@ -225,13 +209,6 @@ namespace Slang
return m_sink;
}
-// ModuleDecl * program = nullptr;
- FuncDecl * function = nullptr;
-
-
- // lexical outer statements
- List<Stmt*> outerStmts;
-
// We need to track what has been `import`ed,
// to avoid importing the same thing more than once
//
@@ -240,7 +217,7 @@ namespace Slang
HashSet<ModuleDecl*> importedModules;
public:
- SemanticsVisitor(
+ SharedSemanticsContext(
Linkage* linkage,
DiagnosticSink* sink)
: m_linkage(linkage)
@@ -252,11 +229,47 @@ namespace Slang
return m_linkage->getSessionImpl();
}
+ };
+
+ struct SemanticsVisitor
+ {
+ SemanticsVisitor(
+ SharedSemanticsContext* shared)
+ : m_shared(shared)
+ {}
+
+ SharedSemanticsContext* m_shared = nullptr;
+
+ SharedSemanticsContext* getShared() { return m_shared; }
+
+ DiagnosticSink* getSink() { return m_shared->getSink(); }
+
+ Session* getSession() { return m_shared->getSession(); }
+
+ Linkage* getLinkage() { return m_shared->m_linkage; }
+ NamePool* getNamePool() { return getLinkage()->getNamePool(); }
+
+ /// Information for tracking one or more outer statements.
+ ///
+ /// During checking of statements, we need to track what
+ /// outer statements are in scope, so that we can resolve
+ /// the target for a `break` or `continue` statement (and
+ /// validate that such statements are only used in contexts
+ /// where such a target exists).
+ ///
+ /// We use a linked list of `OuterStmtInfo` threaded up
+ /// through the recursive call stack to track the statements
+ /// that are lexically surrounding the one we are checking.
+ ///
+ struct OuterStmtInfo
+ {
+ Stmt* stmt;
+ OuterStmtInfo* next;
+ };
+
public:
// Translate Types
- // TODO(tfoley): What is this and why is it needed?
- RefPtr<Type> typeResult;
RefPtr<Expr> TranslateTypeNodeImpl(const RefPtr<Expr> & node);
RefPtr<Type> ExtractTypeFromTypeRepr(const RefPtr<Expr>& typeRepr);
@@ -367,19 +380,38 @@ namespace Slang
DeclRef<GenericDecl> genericDeclRef,
List<RefPtr<Expr>> const& args);
- // This routine is a bottleneck for all declaration checking,
+ // These routines are bottlenecks for semantic checking,
// so that we can add some quality-of-life features for users
// in cases where the compiler crashes
- void dispatchDecl(DeclBase* decl);
- void dispatchStmt(Stmt* stmt);
+ //
+ void dispatchStmt(Stmt* stmt, FuncDecl* parentFunc, OuterStmtInfo* outerStmts);
void dispatchExpr(Expr* expr);
- // Make sure a declaration has been checked, so we can refer to it.
- // Note that this may lead to us recursively invoking checking,
- // so this may not be the best way to handle things.
- void EnsureDecl(RefPtr<Decl> decl, DeclCheckState state);
+ /// Ensure that a declaration has been checked up to some state
+ /// (aka, a phase of semantic checking) so that we can safely
+ /// perform certain operations on it.
+ ///
+ /// Calling `ensureDecl` may cause the type-checker to recursively
+ /// start checking `decl` on top of the stack that is already
+ /// doing other semantic checking. Care should be taken when relying
+ /// on this function to avoid blowing out the stack or (even worse
+ /// creating a circular dependency).
+ ///
+ void ensureDecl(Decl* decl, DeclCheckState state);
+
+ /// Helper routine allowing `ensureDecl` to be called on a `DeclRef`
+ void ensureDecl(DeclRefBase const& declRef, DeclCheckState state)
+ {
+ ensureDecl(declRef.getDecl(), state);
+ }
- void EnusreAllDeclsRec(RefPtr<Decl> decl);
+ /// Helper routine allowing `ensureDecl` to be used on a `DeclBase`
+ ///
+ /// `DeclBase` is the base clas of `Decl` and `DeclGroup`. When
+ /// called on a `DeclGroup` this function just calls `ensureDecl()`
+ /// on each declaration in the group.
+ ///
+ void ensureDeclBase(DeclBase* decl, DeclCheckState state);
// A "proper" type is one that can be used as the type of an expression.
// Put simply, it can be a concrete type like `int`, or a generic
@@ -597,23 +629,11 @@ namespace Slang
RefPtr<Type> toType,
RefPtr<Expr> fromExpr);
- void CheckVarDeclCommon(RefPtr<VarDeclBase> varDecl);
-
// Fill in default substitutions for the 'subtype' part of a type constraint decl
void CheckConstraintSubType(TypeExp& typeExp);
- void CheckGenericConstraintDecl(GenericTypeConstraintDecl* decl);
-
- void checkDecl(Decl* decl);
-
void checkGenericDeclHeader(GenericDecl* genericDecl);
- void visitGenericDecl(GenericDecl* genericDecl);
-
- void visitGenericTypeConstraintDecl(GenericTypeConstraintDecl * genericConstraintDecl);
-
- void visitInheritanceDecl(InheritanceDecl* inheritanceDecl);
-
RefPtr<ConstantIntVal> checkConstantIntVal(
RefPtr<Expr> expr);
@@ -626,14 +646,6 @@ namespace Slang
RefPtr<Expr> expr,
String* outVal);
- void visitSyntaxDecl(SyntaxDecl*);
-
- void visitAttributeDecl(AttributeDecl*);
-
- void visitGenericTypeParamDecl(GenericTypeParamDecl*);
-
- void visitGenericValueParamDecl(GenericValueParamDecl*);
-
void visitModifier(Modifier*);
AttributeDecl* lookUpAttributeDecl(Name* attributeName, Scope* scope);
@@ -655,11 +667,6 @@ namespace Slang
void checkModifiers(ModifiableSyntaxNode* syntaxNode);
- /// Perform checking of interface conformaces for this decl and all its children
- void checkInterfaceConformancesRec(Decl* decl);
-
- void visitModuleDecl(ModuleDecl* programNode);
-
bool doesSignatureMatchRequirement(
DeclRef<CallableDecl> satisfyingMemberDeclRef,
DeclRef<CallableDecl> requiredMemberDeclRef,
@@ -744,29 +751,13 @@ namespace Slang
void checkAggTypeConformance(AggTypeDecl* decl);
- void visitAggTypeDecl(AggTypeDecl* decl);
-
bool isIntegerBaseType(BaseType baseType);
// Validate that `type` is a suitable type to use
// as the tag type for an `enum`
void validateEnumTagType(Type* type, SourceLoc const& loc);
- void visitEnumDecl(EnumDecl* decl);
-
- void visitEnumCaseDecl(EnumCaseDecl* decl);
-
- void visitDeclGroup(DeclGroup* declGroup);
-
- void visitTypeDefDecl(TypeDefDecl* decl);
-
- void visitGlobalGenericParamDecl(GlobalGenericParamDecl* decl);
-
- void visitAssocTypeDecl(AssocTypeDecl* decl);
-
- void checkStmt(Stmt* stmt);
-
- void visitFuncDecl(FuncDecl* functionNode);
+ void checkStmt(Stmt* stmt, FuncDecl* outerFunction, OuterStmtInfo* outerStmts);
void getGenericParams(
GenericDecl* decl,
@@ -788,53 +779,9 @@ namespace Slang
void ValidateFunctionRedeclaration(FuncDecl* funcDecl);
- void visitScopeDecl(ScopeDecl*);
-
- void visitParamDecl(ParamDecl* paramDecl);
-
- void VisitFunctionDeclaration(FuncDecl *functionNode);
-
- void visitDeclStmt(DeclStmt* stmt);
-
- void visitBlockStmt(BlockStmt* stmt);
-
- void visitSeqStmt(SeqStmt* stmt);
-
- template<typename T>
- T* FindOuterStmt();
-
- void visitBreakStmt(BreakStmt *stmt);
-
- void visitContinueStmt(ContinueStmt *stmt);
-
- void PushOuterStmt(Stmt* stmt);
-
- void PopOuterStmt(Stmt* /*stmt*/);
-
RefPtr<Expr> checkPredicateExpr(Expr* expr);
- void visitDoWhileStmt(DoWhileStmt *stmt);
-
- void visitForStmt(ForStmt *stmt);
-
RefPtr<Expr> checkExpressionAndExpectIntegerConstant(RefPtr<Expr> expr, RefPtr<IntVal>* outIntVal);
- void visitCompileTimeForStmt(CompileTimeForStmt* stmt);
-
- void visitSwitchStmt(SwitchStmt* stmt);
-
- void visitCaseStmt(CaseStmt* stmt);
-
- void visitDefaultStmt(DefaultStmt* stmt);
-
- void visitIfStmt(IfStmt *stmt);
-
- void visitUnparsedStmt(UnparsedStmt*);
-
- void visitEmptyStmt(EmptyStmt*);
-
- void visitDiscardStmt(DiscardStmt*);
-
- void visitReturnStmt(ReturnStmt *stmt);
IntegerLiteralValue GetMinBound(RefPtr<IntVal> val);
@@ -842,22 +789,8 @@ namespace Slang
void validateArraySizeForVariable(VarDeclBase* varDecl);
- void visitVarDecl(VarDecl* varDecl);
-
- void visitWhileStmt(WhileStmt *stmt);
-
- void visitExpressionStmt(ExpressionStmt *stmt);
-
- RefPtr<Expr> visitBoolLiteralExpr(BoolLiteralExpr* expr);
- RefPtr<Expr> visitIntegerLiteralExpr(IntegerLiteralExpr* expr);
- RefPtr<Expr> visitFloatingPointLiteralExpr(FloatingPointLiteralExpr* expr);
- RefPtr<Expr> visitStringLiteralExpr(StringLiteralExpr* expr);
-
IntVal* GetIntVal(IntegerLiteralExpr* expr);
- Linkage* getLinkage() { return m_linkage; }
- NamePool* getNamePool() { return getLinkage()->getNamePool(); }
-
Name* getName(String const& text)
{
return getNamePool()->getName(text);
@@ -897,33 +830,18 @@ namespace Slang
RefPtr<Type> elementType,
RefPtr<IntVal> elementCount);
- RefPtr<Expr> visitIndexExpr(IndexExpr* subscriptExpr);
-
- RefPtr<Expr> visitParenExpr(ParenExpr* expr);
-
//
/// Given an immutable `expr` used as an l-value emit a special diagnostic if it was derived from `this`.
void maybeDiagnoseThisNotLValue(Expr* expr);
- RefPtr<Expr> visitAssignExpr(AssignExpr* expr);
-
void registerExtension(ExtensionDecl* decl);
- void visitExtensionDecl(ExtensionDecl* decl);
-
// Figure out what type an initializer/constructor declaration
// is supposed to return. In most cases this is just the type
// declaration that its declaration is nested inside.
RefPtr<Type> findResultTypeForConstructorDecl(ConstructorDecl* decl);
- void visitConstructorDecl(ConstructorDecl* decl);
-
-
- void visitSubscriptDecl(SubscriptDecl* decl);
-
- void visitAccessorDecl(AccessorDecl* decl);
-
//
@@ -1308,28 +1226,83 @@ namespace Slang
RefPtr<Expr> baseExpr,
OverloadResolveContext& context);
- RefPtr<Expr> visitGenericAppExpr(GenericAppExpr* genericAppExpr);
-
/// Check a generic application where the operands have already been checked.
RefPtr<Expr> checkGenericAppWithCheckedArgs(GenericAppExpr* genericAppExpr);
- RefPtr<Expr> visitSharedTypeExpr(SharedTypeExpr* expr);
-
- RefPtr<Expr> visitTaggedUnionTypeExpr(TaggedUnionTypeExpr* expr);
-
RefPtr<Expr> CheckExpr(RefPtr<Expr> expr);
RefPtr<Expr> CheckInvokeExprWithCheckedOperands(InvokeExpr *expr);
+ // Get the type to use when referencing a declaration
+ QualType GetTypeForDeclRef(DeclRef<Decl> declRef);
+
+ //
+ //
+ //
+
+ RefPtr<Expr> MaybeDereference(RefPtr<Expr> inExpr);
+
+ RefPtr<Expr> CheckSwizzleExpr(
+ MemberExpr* memberRefExpr,
+ RefPtr<Type> baseElementType,
+ IntegerLiteralValue baseElementCount);
+
+ RefPtr<Expr> CheckSwizzleExpr(
+ MemberExpr* memberRefExpr,
+ RefPtr<Type> baseElementType,
+ RefPtr<IntVal> baseElementCount);
+
+ // Look up a static member
+ // @param expr Can be StaticMemberExpr or MemberExpr
+ // @param baseExpression Is the underlying type expression determined from resolving expr
+ RefPtr<Expr> _lookupStaticMember(RefPtr<DeclRefExpr> expr, RefPtr<Expr> baseExpression);
+
+ RefPtr<Expr> visitStaticMemberExpr(StaticMemberExpr* expr);
+
+ RefPtr<Expr> lookupMemberResultFailure(
+ DeclRefExpr* expr,
+ QualType const& baseType);
+
+ SharedSemanticsContext & operator = (const SharedSemanticsContext &) = delete;
+
+
+ //
+
+ void importModuleIntoScope(Scope* scope, ModuleDecl* moduleDecl);
+ };
+
+ struct SemanticsExprVisitor
+ : public SemanticsVisitor
+ , ExprVisitor<SemanticsExprVisitor, RefPtr<Expr>>
+ {
+ public:
+ SemanticsExprVisitor(SharedSemanticsContext* shared)
+ : SemanticsVisitor(shared)
+ {}
+
+ RefPtr<Expr> visitBoolLiteralExpr(BoolLiteralExpr* expr);
+ RefPtr<Expr> visitIntegerLiteralExpr(IntegerLiteralExpr* expr);
+ RefPtr<Expr> visitFloatingPointLiteralExpr(FloatingPointLiteralExpr* expr);
+ RefPtr<Expr> visitStringLiteralExpr(StringLiteralExpr* expr);
+
+ RefPtr<Expr> visitIndexExpr(IndexExpr* subscriptExpr);
+
+ RefPtr<Expr> visitParenExpr(ParenExpr* expr);
+
+ RefPtr<Expr> visitAssignExpr(AssignExpr* expr);
+
+ RefPtr<Expr> visitGenericAppExpr(GenericAppExpr* genericAppExpr);
+
+ RefPtr<Expr> visitSharedTypeExpr(SharedTypeExpr* expr);
+
+ RefPtr<Expr> visitTaggedUnionTypeExpr(TaggedUnionTypeExpr* expr);
+
RefPtr<Expr> visitInvokeExpr(InvokeExpr *expr);
RefPtr<Expr> visitVarExpr(VarExpr *expr);
RefPtr<Expr> visitTypeCastExpr(TypeCastExpr * expr);
- // Get the type to use when referencing a declaration
- QualType GetTypeForDeclRef(DeclRef<Decl> declRef);
-
//
// Some syntax nodes should not occur in the concrete input syntax,
// and will only appear *after* checking is complete. We need to
@@ -1355,50 +1328,88 @@ namespace Slang
#undef CASE
- //
- //
- //
+ RefPtr<Expr> visitStaticMemberExpr(StaticMemberExpr* expr);
- RefPtr<Expr> MaybeDereference(RefPtr<Expr> inExpr);
+ RefPtr<Expr> visitMemberExpr(MemberExpr * expr);
- RefPtr<Expr> CheckSwizzleExpr(
- MemberExpr* memberRefExpr,
- RefPtr<Type> baseElementType,
- IntegerLiteralValue baseElementCount);
+ RefPtr<Expr> visitInitializerListExpr(InitializerListExpr* expr);
- RefPtr<Expr> CheckSwizzleExpr(
- MemberExpr* memberRefExpr,
- RefPtr<Type> baseElementType,
- RefPtr<IntVal> baseElementCount);
+ RefPtr<Expr> visitThisExpr(ThisExpr* expr);
+ };
- // Look up a static member
- // @param expr Can be StaticMemberExpr or MemberExpr
- // @param baseExpression Is the underlying type expression determined from resolving expr
- RefPtr<Expr> _lookupStaticMember(RefPtr<DeclRefExpr> expr, RefPtr<Expr> baseExpression);
+ struct SemanticsStmtVisitor
+ : public SemanticsVisitor
+ , StmtVisitor<SemanticsStmtVisitor>
+ {
+ SemanticsStmtVisitor(SharedSemanticsContext* shared, FuncDecl* parentFunc, OuterStmtInfo* outerStmts)
+ : SemanticsVisitor(shared)
+ , m_parentFunc(parentFunc)
+ , m_outerStmts(outerStmts)
+ {}
- RefPtr<Expr> visitStaticMemberExpr(StaticMemberExpr* expr);
+ /// The parent function (if any) that surrounds the statement being checked.
+ // TODO: This should probably be a more general case like `CallableDecl`
+ FuncDecl* m_parentFunc = nullptr;
- RefPtr<Expr> lookupMemberResultFailure(
- DeclRefExpr* expr,
- QualType const& baseType);
+ /// The linked list of lexically surrounding statements.
+ OuterStmtInfo* m_outerStmts = nullptr;
- RefPtr<Expr> visitMemberExpr(MemberExpr * expr);
+ FuncDecl* getParentFunc() { return m_parentFunc; }
- SemanticsVisitor & operator = (const SemanticsVisitor &) = delete;
+ void checkStmt(Stmt* stmt);
+ template<typename T>
+ T* FindOuterStmt();
- //
+ void visitDeclStmt(DeclStmt* stmt);
- RefPtr<Expr> visitInitializerListExpr(InitializerListExpr* expr);
+ void visitBlockStmt(BlockStmt* stmt);
- void importModuleIntoScope(Scope* scope, ModuleDecl* moduleDecl);
+ void visitSeqStmt(SeqStmt* stmt);
- void visitEmptyDecl(EmptyDecl* /*decl*/);
+ void visitBreakStmt(BreakStmt *stmt);
- void visitImportDecl(ImportDecl* decl);
+ void visitContinueStmt(ContinueStmt *stmt);
- // Perform semantic checking of an object-oriented `this`
- // expression.
- RefPtr<Expr> visitThisExpr(ThisExpr* expr);
+ void visitDoWhileStmt(DoWhileStmt *stmt);
+
+ void visitForStmt(ForStmt *stmt);
+
+ void visitCompileTimeForStmt(CompileTimeForStmt* stmt);
+
+ void visitSwitchStmt(SwitchStmt* stmt);
+
+ void visitCaseStmt(CaseStmt* stmt);
+
+ void visitDefaultStmt(DefaultStmt* stmt);
+
+ void visitIfStmt(IfStmt *stmt);
+
+ void visitUnparsedStmt(UnparsedStmt*);
+
+ void visitEmptyStmt(EmptyStmt*);
+
+ void visitDiscardStmt(DiscardStmt*);
+
+ void visitReturnStmt(ReturnStmt *stmt);
+
+ void visitWhileStmt(WhileStmt *stmt);
+
+ void visitExpressionStmt(ExpressionStmt *stmt);
+ };
+
+ struct SemanticsDeclVisitorBase
+ : public SemanticsVisitor
+ {
+ SemanticsDeclVisitorBase(SharedSemanticsContext* shared)
+ : SemanticsVisitor(shared)
+ {}
+
+ void checkBodyStmt(Stmt* stmt, FuncDecl* parentDecl)
+ {
+ checkStmt(stmt, parentDecl, nullptr);
+ }
+
+ void checkModule(ModuleDecl* programNode);
};
}
diff --git a/source/slang/slang-check-modifier.cpp b/source/slang/slang-check-modifier.cpp
index 22db4375a..66be88a1e 100644
--- a/source/slang/slang-check-modifier.cpp
+++ b/source/slang/slang-check-modifier.cpp
@@ -153,7 +153,10 @@ namespace Slang
structAttribDef->ParentDecl->Members.add(attribDecl.Ptr());
structAttribDef->ParentDecl->memberDictionaryIsValid = false;
// do necessary checks on this newly constructed node
- checkDecl(attribDecl.Ptr());
+
+ // TODO: what check state is relevant here?
+ ensureDecl(attribDecl, DeclCheckState::Checked);
+
return attribDecl.Ptr();
}
@@ -368,11 +371,14 @@ namespace Slang
}
else if (auto userDefAttr = as<UserDefinedAttribute>(attr))
{
+
// check arguments against attribute parameters defined in attribClassDecl
Index paramIndex = 0;
auto params = attribClassDecl->getMembersOfType<ParamDecl>();
for (auto paramDecl : params)
{
+ ensureDecl(paramDecl, DeclCheckState::CanUseTypeOfValueDecl);
+
if (paramIndex < attr->args.getCount())
{
auto & arg = attr->args[paramIndex];
diff --git a/source/slang/slang-check-overload.cpp b/source/slang/slang-check-overload.cpp
index dbe6c3aab..a7ae187a7 100644
--- a/source/slang/slang-check-overload.cpp
+++ b/source/slang/slang-check-overload.cpp
@@ -600,7 +600,7 @@ namespace Slang
OverloadResolveContext& context)
{
auto funcDecl = funcDeclRef.getDecl();
- checkDecl(funcDecl);
+ ensureDecl(funcDecl, DeclCheckState::CanUseFuncSignature);
// If this function is a redeclaration,
// then we don't want to include it multiple times,
@@ -659,7 +659,7 @@ namespace Slang
OverloadResolveContext& context,
RefPtr<Type> resultType)
{
- checkDecl(ctorDeclRef.getDecl());
+ ensureDecl(ctorDeclRef, DeclCheckState::CanUseFuncSignature);
// `typeItem` refers to the type being constructed (the thing
// that was applied as a function) so we need to construct
@@ -684,7 +684,7 @@ namespace Slang
DeclRef<GenericDecl> genericDeclRef,
OverloadResolveContext& context)
{
- checkDecl(genericDeclRef.getDecl());
+ ensureDecl(genericDeclRef, DeclCheckState::CanSpecializeGeneric);
ConstraintSystem constraints;
constraints.loc = context.loc;
@@ -1341,7 +1341,7 @@ namespace Slang
{
if (auto genericDeclRef = baseItem.declRef.as<GenericDecl>())
{
- checkDecl(genericDeclRef.getDecl());
+ ensureDecl(genericDeclRef, DeclCheckState::CanSpecializeGeneric);
OverloadCandidate candidate;
candidate.flavor = OverloadCandidate::Flavor::Generic;
@@ -1376,7 +1376,7 @@ namespace Slang
}
}
- RefPtr<Expr> SemanticsVisitor::visitGenericAppExpr(GenericAppExpr* genericAppExpr)
+ RefPtr<Expr> SemanticsExprVisitor::visitGenericAppExpr(GenericAppExpr* genericAppExpr)
{
// Start by checking the base expression and arguments.
auto& baseExpr = genericAppExpr->FunctionExpr;
diff --git a/source/slang/slang-check-shader.cpp b/source/slang/slang-check-shader.cpp
index 2ac449e83..333690b36 100644
--- a/source/slang/slang-check-shader.cpp
+++ b/source/slang/slang-check-shader.cpp
@@ -1415,7 +1415,8 @@ static bool doesParameterMatch(
{
SLANG_ASSERT(argCount == getSpecializationParamCount());
- SemanticsVisitor visitor(getLinkage(), sink);
+ SharedSemanticsContext semanticsContext(getLinkage(), sink);
+ SemanticsVisitor visitor(&semanticsContext);
RefPtr<Module::ModuleSpecializationInfo> specializationInfo = new Module::ModuleSpecializationInfo();
@@ -1588,7 +1589,8 @@ static bool doesParameterMatch(
auto args = inArgs;
auto argCount = inArgCount;
- SemanticsVisitor visitor(getLinkage(), sink);
+ SharedSemanticsContext sharedSemanticsContext(getLinkage(), sink);
+ SemanticsVisitor visitor(&sharedSemanticsContext);
// The first N arguments will be for the explicit generic parameters
// of the entry point (if it has any).
@@ -1737,9 +1739,11 @@ static bool doesParameterMatch(
//
auto linkage = endToEndReq->getLinkage();
auto sink = endToEndReq->getSink();
- SemanticsVisitor semantics(
+
+ SharedSemanticsContext sharedSemanticsContext(
linkage,
sink);
+ SemanticsVisitor semantics(&sharedSemanticsContext);
// We will be looping over the generic argument strings
// that the user provided via the API (or command line),
@@ -1782,7 +1786,8 @@ static bool doesParameterMatch(
// TODO: We should cache and re-use specialized types
// when the exact same arguments are provided again later.
- SemanticsVisitor visitor(this, sink);
+ SharedSemanticsContext sharedSemanticsContext(this, sink);
+ SemanticsVisitor visitor(&sharedSemanticsContext);
SpecializationParams specializationParams;
_collectExistentialSpecializationParamsRec(specializationParams, unspecializedType);
@@ -1839,7 +1844,7 @@ static bool doesParameterMatch(
// We have an appropriate number of arguments for the global specialization parameters,
// and now we need to check that the arguments conform to the declared constraints.
//
- SemanticsVisitor visitor(linkage, sink);
+ SharedSemanticsContext visitor(linkage, sink);
List<SpecializationArg> specializationArgs;
_extractSpecializationArgs(unspecializedProgram, specializationArgExprs, specializationArgs, sink);
diff --git a/source/slang/slang-check-stmt.cpp b/source/slang/slang-check-stmt.cpp
index 5e9676a23..7a157a1fd 100644
--- a/source/slang/slang-check-stmt.cpp
+++ b/source/slang/slang-check-stmt.cpp
@@ -5,34 +5,64 @@
namespace Slang
{
- void SemanticsVisitor::checkStmt(Stmt* stmt)
+ namespace
+ {
+ /// RAII-like type for establishing an "outer" statement during nested checks.
+ ///
+ /// The `SemanticsStmtVisitor` maintains a linked list of outer statements
+ /// using `OuterStmtInfo` records stored on the recursive call stack during
+ /// checking. This type creates a sub-`SemanticsStmtVisitor` that has one
+ /// additional outer statement added to the stack of outer statements.
+ ///
+ /// The outer statements are used to validate and resolve things like
+ /// the target of `break` or `continue` statements.
+ ///
+ struct WithOuterStmt : public SemanticsStmtVisitor
+ {
+ public:
+ WithOuterStmt(SemanticsStmtVisitor* visitor, Stmt* outerStmt)
+ : SemanticsStmtVisitor(*visitor)
+ {
+ m_parentFunc = visitor->m_parentFunc;
+
+ m_outerStmt.next = visitor->m_outerStmts;
+ m_outerStmt.stmt = outerStmt;
+ m_outerStmts = &m_outerStmt;
+ }
+
+ private:
+ OuterStmtInfo m_outerStmt;
+ };
+ }
+
+ void SemanticsVisitor::checkStmt(Stmt* stmt, FuncDecl* parentDecl, OuterStmtInfo* outerStmts)
{
if (!stmt) return;
- dispatchStmt(stmt);
+ dispatchStmt(stmt, parentDecl, outerStmts);
checkModifiers(stmt);
}
- void SemanticsVisitor::visitDeclStmt(DeclStmt* stmt)
+ void SemanticsStmtVisitor::visitDeclStmt(DeclStmt* stmt)
{
- // We directly dispatch here instead of using `EnsureDecl()` for two
- // reasons:
- //
- // 1. We expect that a local declaration won't have been referenced
- // before it is declared, so that we can just check things in-order
+ // When we encounter a declaration during statement checking,
+ // we expect that it hasn't been checked yet (because otherwise
+ // it would be referenced before its declaration point), but
+ // we will bottleneck through the `ensureDecl()` path anyway,
+ // to unify with the rest of semantic checking.
//
- // 2. `EnsureDecl()` is specialized for `Decl*` instead of `DeclBase*`
- // and trying to special case `DeclGroup*` here feels silly.
+ // TODO: This logic might not suffice for something like a
+ // local `struct` declaration, where it would have members
+ // that need to be recursively checked.
//
- dispatchDecl(stmt->decl);
- checkModifiers(stmt->decl);
+ ensureDeclBase(stmt->decl, DeclCheckState::Checked);
}
- void SemanticsVisitor::visitBlockStmt(BlockStmt* stmt)
+ void SemanticsStmtVisitor::visitBlockStmt(BlockStmt* stmt)
{
checkStmt(stmt->body);
}
- void SemanticsVisitor::visitSeqStmt(SeqStmt* stmt)
+ void SemanticsStmtVisitor::visitSeqStmt(SeqStmt* stmt)
{
for(auto ss : stmt->stmts)
{
@@ -40,13 +70,17 @@ namespace Slang
}
}
+ void SemanticsStmtVisitor::checkStmt(Stmt* stmt)
+ {
+ SemanticsVisitor::checkStmt(stmt, m_parentFunc, m_outerStmts);
+ }
+
template<typename T>
- T* SemanticsVisitor::FindOuterStmt()
+ T* SemanticsStmtVisitor::FindOuterStmt()
{
- const Index outerStmtCount = outerStmts.getCount();
- for (Index ii = outerStmtCount; ii > 0; --ii)
+ for(auto outerStmtInfo = m_outerStmts; outerStmtInfo; outerStmtInfo = outerStmtInfo->next)
{
- auto outerStmt = outerStmts[ii-1];
+ auto outerStmt = outerStmtInfo->stmt;
auto found = as<T>(outerStmt);
if (found)
return found;
@@ -54,7 +88,7 @@ namespace Slang
return nullptr;
}
- void SemanticsVisitor::visitBreakStmt(BreakStmt *stmt)
+ void SemanticsStmtVisitor::visitBreakStmt(BreakStmt *stmt)
{
auto outer = FindOuterStmt<BreakableStmt>();
if (!outer)
@@ -64,7 +98,7 @@ namespace Slang
stmt->parentStmt = outer;
}
- void SemanticsVisitor::visitContinueStmt(ContinueStmt *stmt)
+ void SemanticsStmtVisitor::visitContinueStmt(ContinueStmt *stmt)
{
auto outer = FindOuterStmt<LoopStmt>();
if (!outer)
@@ -74,16 +108,6 @@ namespace Slang
stmt->parentStmt = outer;
}
- void SemanticsVisitor::PushOuterStmt(Stmt* stmt)
- {
- outerStmts.add(stmt);
- }
-
- void SemanticsVisitor::PopOuterStmt(Stmt* /*stmt*/)
- {
- outerStmts.removeAt(outerStmts.getCount() - 1);
- }
-
RefPtr<Expr> SemanticsVisitor::checkPredicateExpr(Expr* expr)
{
RefPtr<Expr> e = expr;
@@ -92,18 +116,18 @@ namespace Slang
return e;
}
- void SemanticsVisitor::visitDoWhileStmt(DoWhileStmt *stmt)
+ void SemanticsStmtVisitor::visitDoWhileStmt(DoWhileStmt *stmt)
{
- PushOuterStmt(stmt);
- stmt->Predicate = checkPredicateExpr(stmt->Predicate);
- checkStmt(stmt->Statement);
+ WithOuterStmt subContext(this, stmt);
- PopOuterStmt(stmt);
+ stmt->Predicate = checkPredicateExpr(stmt->Predicate);
+ subContext.checkStmt(stmt->Statement);
}
- void SemanticsVisitor::visitForStmt(ForStmt *stmt)
+ void SemanticsStmtVisitor::visitForStmt(ForStmt *stmt)
{
- PushOuterStmt(stmt);
+ WithOuterStmt subContext(this, stmt);
+
checkStmt(stmt->InitialStatement);
if (stmt->PredicateExpression)
{
@@ -113,9 +137,7 @@ namespace Slang
{
stmt->SideEffectExpression = CheckExpr(stmt->SideEffectExpression);
}
- checkStmt(stmt->Statement);
-
- PopOuterStmt(stmt);
+ subContext.checkStmt(stmt->Statement);
}
RefPtr<Expr> SemanticsVisitor::checkExpressionAndExpectIntegerConstant(RefPtr<Expr> expr, RefPtr<IntVal>* outIntVal)
@@ -127,9 +149,9 @@ namespace Slang
return expr;
}
- void SemanticsVisitor::visitCompileTimeForStmt(CompileTimeForStmt* stmt)
+ void SemanticsStmtVisitor::visitCompileTimeForStmt(CompileTimeForStmt* stmt)
{
- PushOuterStmt(stmt);
+ WithOuterStmt subContext(this, stmt);
stmt->varDecl->type.type = getSession()->getIntType();
addModifier(stmt->varDecl, new ConstModifier());
@@ -154,27 +176,23 @@ namespace Slang
stmt->rangeBeginVal = rangeBeginVal;
stmt->rangeEndVal = rangeEndVal;
- checkStmt(stmt->body);
-
-
- PopOuterStmt(stmt);
+ subContext.checkStmt(stmt->body);
}
- void SemanticsVisitor::visitSwitchStmt(SwitchStmt* stmt)
+ void SemanticsStmtVisitor::visitSwitchStmt(SwitchStmt* stmt)
{
- PushOuterStmt(stmt);
+ WithOuterStmt subContext(this, stmt);
+
// TODO(tfoley): need to coerce condition to an integral type...
stmt->condition = CheckExpr(stmt->condition);
- checkStmt(stmt->body);
+ subContext.checkStmt(stmt->body);
// TODO(tfoley): need to check that all case tags are unique
// TODO(tfoley): check that there is at most one `default` clause
-
- PopOuterStmt(stmt);
}
- void SemanticsVisitor::visitCaseStmt(CaseStmt* stmt)
+ void SemanticsStmtVisitor::visitCaseStmt(CaseStmt* stmt)
{
// TODO(tfoley): Need to coerce to type being switch on,
// and ensure that value is a compile-time constant
@@ -195,7 +213,7 @@ namespace Slang
stmt->parentStmt = switchStmt;
}
- void SemanticsVisitor::visitDefaultStmt(DefaultStmt* stmt)
+ void SemanticsStmtVisitor::visitDefaultStmt(DefaultStmt* stmt)
{
auto switchStmt = FindOuterStmt<SwitchStmt>();
if (!switchStmt)
@@ -205,30 +223,31 @@ namespace Slang
stmt->parentStmt = switchStmt;
}
- void SemanticsVisitor::visitIfStmt(IfStmt *stmt)
+ void SemanticsStmtVisitor::visitIfStmt(IfStmt *stmt)
{
stmt->Predicate = checkPredicateExpr(stmt->Predicate);
checkStmt(stmt->PositiveStatement);
checkStmt(stmt->NegativeStatement);
}
- void SemanticsVisitor::visitUnparsedStmt(UnparsedStmt*)
+ void SemanticsStmtVisitor::visitUnparsedStmt(UnparsedStmt*)
{
// Nothing to do
}
- void SemanticsVisitor::visitEmptyStmt(EmptyStmt*)
+ void SemanticsStmtVisitor::visitEmptyStmt(EmptyStmt*)
{
// Nothing to do
}
- void SemanticsVisitor::visitDiscardStmt(DiscardStmt*)
+ void SemanticsStmtVisitor::visitDiscardStmt(DiscardStmt*)
{
// Nothing to do
}
- void SemanticsVisitor::visitReturnStmt(ReturnStmt *stmt)
+ void SemanticsStmtVisitor::visitReturnStmt(ReturnStmt *stmt)
{
+ auto function = getParentFunc();
if (!stmt->Expression)
{
if (function && !function->ReturnType.Equals(getSession()->getVoidType()))
@@ -257,15 +276,14 @@ namespace Slang
}
}
- void SemanticsVisitor::visitWhileStmt(WhileStmt *stmt)
+ void SemanticsStmtVisitor::visitWhileStmt(WhileStmt *stmt)
{
- PushOuterStmt(stmt);
+ WithOuterStmt subContext(this, stmt);
stmt->Predicate = checkPredicateExpr(stmt->Predicate);
- checkStmt(stmt->Statement);
- PopOuterStmt(stmt);
+ subContext.checkStmt(stmt->Statement);
}
- void SemanticsVisitor::visitExpressionStmt(ExpressionStmt *stmt)
+ void SemanticsStmtVisitor::visitExpressionStmt(ExpressionStmt *stmt)
{
stmt->Expression = CheckExpr(stmt->Expression);
}
diff --git a/source/slang/slang-check-type.cpp b/source/slang/slang-check-type.cpp
index 0786d3166..ea808e456 100644
--- a/source/slang/slang-check-type.cpp
+++ b/source/slang/slang-check-type.cpp
@@ -11,9 +11,12 @@ namespace Slang
TypeExp typeExp,
DiagnosticSink* sink)
{
- SemanticsVisitor visitor(
+ SharedSemanticsContext sharedSemanticsContext(
linkage,
sink);
+ SemanticsVisitor visitor(&sharedSemanticsContext);
+
+
auto typeOut = visitor.CheckProperType(typeExp);
return typeOut.type;
}
@@ -185,7 +188,7 @@ namespace Slang
//
auto genericDeclRef = genericDeclRefType->GetDeclRef();
- checkDecl(genericDeclRef.decl);
+ ensureDecl(genericDeclRef, DeclCheckState::CanSpecializeGeneric);
List<RefPtr<Expr>> args;
for (RefPtr<Decl> member : genericDeclRef.getDecl()->Members)
{
@@ -331,7 +334,7 @@ namespace Slang
declRef).as<VectorExpressionType>();
}
- RefPtr<Expr> SemanticsVisitor::visitSharedTypeExpr(SharedTypeExpr* expr)
+ RefPtr<Expr> SemanticsExprVisitor::visitSharedTypeExpr(SharedTypeExpr* expr)
{
if (!expr->type.Ptr())
{
@@ -341,7 +344,7 @@ namespace Slang
return expr;
}
- RefPtr<Expr> SemanticsVisitor::visitTaggedUnionTypeExpr(TaggedUnionTypeExpr* expr)
+ RefPtr<Expr> SemanticsExprVisitor::visitTaggedUnionTypeExpr(TaggedUnionTypeExpr* expr)
{
// We have an expression of the form `__TaggedUnion(A, B, ...)`
// which will evaluate to a tagged-union type over `A`, `B`, etc.
diff --git a/source/slang/slang-check.cpp b/source/slang/slang-check.cpp
index 2d8d916a5..704d37d01 100644
--- a/source/slang/slang-check.cpp
+++ b/source/slang/slang-check.cpp
@@ -156,39 +156,27 @@ namespace Slang
void checkTranslationUnit(
TranslationUnitRequest* translationUnit)
{
- SemanticsVisitor visitor(
+ SharedSemanticsContext sharedSemanticsContext(
translationUnit->compileRequest->getLinkage(),
translationUnit->compileRequest->getSink());
+ SemanticsDeclVisitorBase visitor(&sharedSemanticsContext);
+
// Apply the visitor to do the main semantic
// checking that is required on all declarations
// in the translation unit.
- visitor.checkDecl(translationUnit->getModuleDecl());
- translationUnit->getModule()->_collectShaderParams();
- }
+ visitor.checkModule(translationUnit->getModuleDecl());
- void SemanticsVisitor::dispatchDecl(DeclBase* decl)
- {
- try
- {
- DeclVisitor::dispatch(decl);
- }
- // Don't emit any context message for an explicit `AbortCompilationException`
- // because it should only happen when an error is already emitted.
- catch(AbortCompilationException&) { throw; }
- catch(...)
- {
- getSink()->noteInternalErrorLoc(decl->loc);
- throw;
- }
+ translationUnit->getModule()->_collectShaderParams();
}
- void SemanticsVisitor::dispatchStmt(Stmt* stmt)
+ void SemanticsVisitor::dispatchStmt(Stmt* stmt, FuncDecl* parentFunc, OuterStmtInfo* outerStmts)
{
+ SemanticsStmtVisitor visitor(getShared(), parentFunc, outerStmts);
try
{
- StmtVisitor::dispatch(stmt);
+ visitor.dispatch(stmt);
}
catch(AbortCompilationException&) { throw; }
catch(...)
@@ -200,9 +188,10 @@ namespace Slang
void SemanticsVisitor::dispatchExpr(Expr* expr)
{
+ SemanticsExprVisitor visitor(getShared());
try
{
- ExprVisitor::dispatch(expr);
+ visitor.dispatch(expr);
}
catch(AbortCompilationException&) { throw; }
catch(...)
diff --git a/source/slang/slang-check.h b/source/slang/slang-check.h
index 0d6342ca7..94deba1aa 100644
--- a/source/slang/slang-check.h
+++ b/source/slang/slang-check.h
@@ -35,4 +35,5 @@ namespace Slang
DiagnosticSink* sink);
bool isGlobalShaderParameter(VarDeclBase* decl);
+ bool isFromStdLib(Decl* decl);
}
diff --git a/source/slang/slang-lookup.cpp b/source/slang/slang-lookup.cpp
index 0a77a259a..f46a15d02 100644
--- a/source/slang/slang-lookup.cpp
+++ b/source/slang/slang-lookup.cpp
@@ -4,12 +4,12 @@
namespace Slang {
-void checkDecl(SemanticsVisitor* visitor, Decl* decl);
+void ensureDecl(SemanticsVisitor* visitor, Decl* decl, DeclCheckState state);
//
DeclRef<ExtensionDecl> ApplyExtensionToType(
- SemanticsVisitor* semantics,
+ SemanticsVisitor* semantics,
ExtensionDecl* extDecl,
RefPtr<Type> type);
@@ -241,6 +241,11 @@ DeclRef<Decl> maybeSpecializeInterfaceDeclRef(
{
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.
//
@@ -358,6 +363,11 @@ void DoLocalLookupImpl(
// Consider lookup via extension
if( auto aggTypeDeclRef = containerDeclRef.as<AggTypeDecl>() )
{
+ if (request.semantics)
+ {
+ ensureDecl(request.semantics, containerDeclRef.getDecl(), DeclCheckState::ReadyForLookup);
+ }
+
RefPtr<Type> type = DeclRefType::Create(
session,
aggTypeDeclRef);
@@ -397,6 +407,8 @@ void DoLocalLookupImpl(
RefPtr<DeclRefType> targetDeclRefType;
if (auto extDeclRef = containerDeclRef.as<ExtensionDecl>())
{
+ ensureDecl(request.semantics, extDeclRef.getDecl(), DeclCheckState::CanUseExtensionTargetType);
+
targetDeclRefType = as<DeclRefType>(extDeclRef.getDecl()->targetType);
SLANG_ASSERT(targetDeclRefType);
int diff = 0;
@@ -414,7 +426,7 @@ void DoLocalLookupImpl(
auto baseInterfaces = getMembersOfType<InheritanceDecl>(containerDeclRef);
for (auto inheritanceDeclRef : baseInterfaces)
{
- checkDecl(request.semantics, inheritanceDeclRef.decl);
+ ensureDecl(request.semantics, inheritanceDeclRef.getDecl(), DeclCheckState::CanUseBaseOfInheritanceDecl);
auto baseType = inheritanceDeclRef.getDecl()->base.type.dynamicCast<DeclRefType>();
SLANG_ASSERT(baseType);
@@ -556,7 +568,7 @@ LookupResult lookUpLocal(
SemanticsVisitor* semantics,
Name* name,
DeclRef<ContainerDecl> containerDeclRef,
- LookupMask mask)
+ LookupMask mask)
{
LookupRequest request;
request.semantics = semantics;
diff --git a/source/slang/slang-lookup.h b/source/slang/slang-lookup.h
index 705b952f3..76a097d8c 100644
--- a/source/slang/slang-lookup.h
+++ b/source/slang/slang-lookup.h
@@ -31,7 +31,7 @@ LookupResult lookUpLocal(
SemanticsVisitor* semantics,
Name* name,
DeclRef<ContainerDecl> containerDeclRef,
- LookupMask mask = LookupMask::Default);
+ LookupMask mask = LookupMask::Default);
// Perform member lookup in the context of a type
LookupResult lookUpMember(
diff --git a/source/slang/slang-syntax-base-defs.h b/source/slang/slang-syntax-base-defs.h
index 2f7c8b1fa..afec117ca 100644
--- a/source/slang/slang-syntax-base-defs.h
+++ b/source/slang/slang-syntax-base-defs.h
@@ -275,7 +275,7 @@ ABSTRACT_SYNTAX_CLASS(Decl, DeclBase)
)
- FIELD_INIT(DeclCheckState, checkState, DeclCheckState::Unchecked)
+ FIELD_INIT(DeclCheckStateExt, checkState, DeclCheckState::Unchecked)
// The next declaration defined in the same container with the same name
DECL_FIELD(Decl*, nextInContainerWithSameName RAW(= nullptr))
@@ -284,8 +284,8 @@ ABSTRACT_SYNTAX_CLASS(Decl, DeclBase)
bool IsChecked(DeclCheckState state) { return checkState >= state; }
void SetCheckState(DeclCheckState state)
{
- SLANG_RELEASE_ASSERT(state >= checkState);
- checkState = state;
+ SLANG_RELEASE_ASSERT(state >= checkState.getState());
+ checkState.setState(state);
}
)
END_SYNTAX_CLASS()
diff --git a/source/slang/slang-syntax.cpp b/source/slang/slang-syntax.cpp
index c4152d78c..e41e5517c 100644
--- a/source/slang/slang-syntax.cpp
+++ b/source/slang/slang-syntax.cpp
@@ -2021,7 +2021,15 @@ void Type::accept(IValVisitor* visitor, void* extra)
RefPtr<Decl> decl,
RefPtr<MagicTypeModifier> modifier)
{
- session->magicDecls[modifier->name] = decl.Ptr();
+ // In some cases the modifier will have been applied to the
+ // "inner" declaration of a `GenericDecl`, but what we
+ // actually want to register is the generic itself.
+ //
+ auto declToRegister = decl;
+ if(auto genericDecl = as<GenericDecl>(decl->ParentDecl))
+ declToRegister = genericDecl;
+
+ session->magicDecls[modifier->name] = declToRegister.Ptr();
}
RefPtr<Decl> findMagicDecl(
diff --git a/source/slang/slang-syntax.h b/source/slang/slang-syntax.h
index 88a2ca847..8c07855e4 100644
--- a/source/slang/slang-syntax.h
+++ b/source/slang/slang-syntax.h
@@ -304,22 +304,121 @@ namespace Slang
// This fill assert-fail if the object doesn't represent a literal value.
IntegerLiteralValue GetIntVal(RefPtr<IntVal> val);
- // Represents how much checking has been applied to a declaration.
+ /// Represents how much checking has been applied to a declaration.
enum class DeclCheckState : uint8_t
{
- // The declaration has been parsed, but not checked
+ /// The declaration has been parsed, but
+ /// is otherwise completely unchecked.
+ ///
Unchecked,
- // We are in the process of checking the declaration "header"
- // (those parts of the declaration needed in order to
- // reference it)
- CheckingHeader,
+ /// Basic checks on the modifiers of the declaration have been applied.
+ ///
+ /// For example, when a declaration has attributes, the transformation
+ /// of an attribute from the parsed-but-unchecked form into a checked
+ /// form (in which it has the appropriate C++ subclass) happens here.
+ ///
+ ModifiersChecked,
+
+ /// The declaration's basic signature has been checked to the point that
+ /// it is ready to be referenced in other places.
+ ///
+ /// For a value declaration like a variable or function, this means that
+ /// the type of the declaration can be queried.
+ ///
+ /// For a type declaration like a `struct` or `typedef` this means
+ /// that a `Type` referring to that declaration can be formed.
+ ///
+ ReadyForReference,
+
+ /// The declaration is ready for lookup operations to be performed.
+ ///
+ /// For type declarations (e.g., aggregate types, generic type parameters)
+ /// this means that any base type or constraint clauses have been
+ /// sufficiently checked so that we can enumerate the inheritance
+ /// hierarchy of the type and discover all its members.
+ ///
+ ReadyForLookup,
+
+ /// Any conformance declared on the declaration have been validated.
+ ///
+ /// In particular, this step means that a "witness table" has been
+ /// created to show how a type satisfies the requirements of any
+ /// interfaces it conforms to.
+ ///
+ ReadyForConformances,
+
+ /// The declaration is fully checked.
+ ///
+ /// This step includes any validation of the declaration that is
+ /// immaterial to clients code using the declaration, but that is
+ /// nonetheless relevant to checking correctness.
+ ///
+ /// The canonical example here is checking the body of functions.
+ /// Client code cannot depend on *how* a function is implemented,
+ /// but we still need to (eventually) check the bodies of all
+ /// functions, so it belongs in the last phase of checking.
+ ///
+ Checked,
- // We are done checking the declaration header.
- CheckedHeader,
+ // For convenience at sites that call `ensureDecl()`, we define
+ // some aliases for the above states that are expressed in terms
+ // of what client code needs to be able to do with a declaration.
+ //
+ // These aliases can be changed over time if we decide to add
+ // more phases to semantic checking.
+
+ CanEnumerateBases = ReadyForLookup,
+ CanUseBaseOfInheritanceDecl = ReadyForLookup,
+ CanUseTypeOfValueDecl = ReadyForReference,
+ CanUseExtensionTargetType = ReadyForLookup,
+ CanUseAsType = ReadyForReference,
+ CanUseFuncSignature = ReadyForReference,
+ CanSpecializeGeneric = ReadyForReference,
+ CanReadInterfaceRequirements = ReadyForLookup,
+ };
- // We have checked the declaration fully.
- Checked,
+ /// A `DeclCheckState` plus a bit to track whether a declaration is currently being checked.
+ struct DeclCheckStateExt
+ {
+ public:
+ DeclCheckStateExt() {}
+ DeclCheckStateExt(DeclCheckState state)
+ : m_raw(uint8_t(state))
+ {}
+
+ enum : uint8_t
+ {
+ /// A flag to indicate that a declaration is being checked.
+ ///
+ /// The value of this flag is chosen so that it can be
+ /// represented in the bits of a `DeclCheckState` without
+ /// colliding with the bits that represent actual states.
+ ///
+ kBeingCheckedBit = 0x80,
+ };
+
+ DeclCheckState getState() const { return DeclCheckState(m_raw & ~kBeingCheckedBit); }
+ void setState(DeclCheckState state)
+ {
+ m_raw = (m_raw & kBeingCheckedBit) | uint8_t(state);
+ }
+
+ bool isBeingChecked() const { return (m_raw & kBeingCheckedBit) != 0; }
+
+ void setIsBeingChecked(bool isBeingChecked)
+ {
+ m_raw = (m_raw & ~kBeingCheckedBit)
+ | (isBeingChecked ? kBeingCheckedBit : 0);
+ }
+
+ bool operator>=(DeclCheckState state) const
+ {
+ return getState() >= state;
+ }
+
+ private:
+ uint8_t m_raw = 0;
};
void addModifier(