summaryrefslogtreecommitdiff
path: root/source/slang/slang-check-decl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/slang-check-decl.cpp')
-rw-r--r--source/slang/slang-check-decl.cpp1414
1 files changed, 760 insertions, 654 deletions
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;
+ }
}
}