summaryrefslogtreecommitdiff
path: root/source/slang/slang-check-decl.cpp
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2019-10-25 14:19:56 -0700
committerGitHub <noreply@github.com>2019-10-25 14:19:56 -0700
commitc886ca811975e91cedca898a561ff65a5663272d (patch)
tree43dbae0f34972f293144dde9edaadef413462508 /source/slang/slang-check-decl.cpp
parent7cf9b65c3836cdc17e6761bfd76383564ff0ec9d (diff)
Refactor semantic checking code into more files (#1097)
The semantic checking logic was all inside `slang-check.cpp` and as a result this was a monster file that was extremely hard to follow. This change splits `slang-check.cpp` into several smaller files, although some of the resulting files are still quite large. This change attempts to be a copy-paste job as much as possible and does *not* perform any cleanup on naming, structure, duplication, etc. in the code it deal with. No function bodies or signatures have been touched.
Diffstat (limited to 'source/slang/slang-check-decl.cpp')
-rw-r--r--source/slang/slang-check-decl.cpp2745
1 files changed, 2745 insertions, 0 deletions
diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp
new file mode 100644
index 000000000..581cefdcb
--- /dev/null
+++ b/source/slang/slang-check-decl.cpp
@@ -0,0 +1,2745 @@
+// slang-check-decl.cpp
+#include "slang-check-impl.h"
+
+// This file constaints the semantic checking logic and
+// related queries for declarations.
+//
+// Because declarations are the top-level construct
+// of the AST (in turn containing all the statements,
+// types, and expressions), the declaration-checking
+// logic also orchestrates the overall flow and how
+// and when things get checked.
+
+#include "slang-lookup.h"
+
+namespace Slang
+{
+
+ /// Should the given `decl` nested in `parentDecl` be treated as a static rather than instance declaration?
+ bool isEffectivelyStatic(
+ Decl* decl,
+ ContainerDecl* parentDecl)
+ {
+ // Things at the global scope are always "members" of their module.
+ //
+ if(as<ModuleDecl>(parentDecl))
+ return false;
+
+ // Anything explicitly marked `static` and not at module scope
+ // counts as a static rather than instance declaration.
+ //
+ if(decl->HasModifier<HLSLStaticModifier>())
+ return true;
+
+ // Next we need to deal with cases where a declaration is
+ // effectively `static` even if the language doesn't make
+ // the user say so. Most languages make the default assumption
+ // that nested types are `static` even if they don't say
+ // so (Java is an exception here, perhaps due to some
+ // influence from the Scandanavian OOP tradition of Beta/gbeta).
+ //
+ if(as<AggTypeDecl>(decl))
+ return true;
+ if(as<SimpleTypeDecl>(decl))
+ return true;
+
+ // Things nested inside functions may have dependencies
+ // on values from the enclosing scope, but this needs to
+ // be dealt with via "capture" so they are also effectively
+ // `static`
+ //
+ if(as<FunctionDeclBase>(parentDecl))
+ return true;
+
+ // Type constraint declarations are used in member-reference
+ // context as a form of casting operation, so we treat them
+ // as if they are instance members. This is a bit of a hack,
+ // but it achieves the result we want until we have an
+ // explicit representation of up-cast operations in the
+ // AST.
+ //
+ if(as<TypeConstraintDecl>(decl))
+ return false;
+
+ return false;
+ }
+
+ bool isEffectivelyStatic(
+ Decl* decl)
+ {
+ // For the purposes of an ordinary declaration, when determining if
+ // it is static or per-instance, the "parent" declaration we really
+ // care about is the next outer non-generic declaration.
+ //
+ // TODO: This idiom of getting the "next outer non-generic declaration"
+ // comes up just enough that we should probably have a convenience
+ // function for it.
+
+ auto parentDecl = decl->ParentDecl;
+ if(auto genericDecl = as<GenericDecl>(parentDecl))
+ parentDecl = genericDecl->ParentDecl;
+
+ return isEffectivelyStatic(decl, parentDecl);
+ }
+
+ /// Is `decl` a global shader parameter declaration?
+ bool isGlobalShaderParameter(VarDeclBase* decl)
+ {
+ // A global shader parameter must be declared at global (module) scope.
+ //
+ if(!as<ModuleDecl>(decl->ParentDecl)) return false;
+
+ // A global variable marked `static` indicates a traditional
+ // global variable (albeit one that is implicitly local to
+ // the translation unit)
+ //
+ if(decl->HasModifier<HLSLStaticModifier>()) return false;
+
+ // The `groupshared` modifier indicates that a variable cannot
+ // be a shader parameters, but is instead transient storage
+ // allocated for the duration of a thread-group's execution.
+ //
+ if(decl->HasModifier<HLSLGroupSharedModifier>()) return false;
+
+ return true;
+ }
+
+ // Get the type to use when referencing a declaration
+ QualType getTypeForDeclRef(
+ Session* session,
+ SemanticsVisitor* sema,
+ DiagnosticSink* sink,
+ DeclRef<Decl> declRef,
+ RefPtr<Type>* outTypeResult)
+ {
+ if( sema )
+ {
+ sema->checkDecl(declRef.getDecl());
+ }
+
+ // We need to insert an appropriate type for the expression, based on
+ // what we found.
+ if (auto varDeclRef = declRef.as<VarDeclBase>())
+ {
+ QualType qualType;
+ qualType.type = GetType(varDeclRef);
+
+ bool isLValue = true;
+ if(varDeclRef.getDecl()->FindModifier<ConstModifier>())
+ isLValue = false;
+
+ // Global-scope shader parameters should not be writable,
+ // since they are effectively program inputs.
+ //
+ // TODO: We could eventually treat a mutable global shader
+ // parameter as a shorthand for an immutable parameter and
+ // a global variable that gets initialized from that parameter,
+ // but in order to do so we'd need to support global variables
+ // with resource types better in the back-end.
+ //
+ if(isGlobalShaderParameter(varDeclRef.getDecl()))
+ isLValue = false;
+
+ // Variables declared with `let` are always immutable.
+ if(varDeclRef.is<LetDecl>())
+ isLValue = false;
+
+ // Generic value parameters are always immutable
+ if(varDeclRef.is<GenericValueParamDecl>())
+ isLValue = false;
+
+ // Function parameters declared in the "modern" style
+ // are immutable unless they have an `out` or `inout` modifier.
+ if(varDeclRef.is<ModernParamDecl>())
+ {
+ // Note: the `inout` modifier AST class inherits from
+ // the class for the `out` modifier so that we can
+ // make simple checks like this.
+ //
+ if( !varDeclRef.getDecl()->HasModifier<OutModifier>() )
+ {
+ isLValue = false;
+ }
+ }
+
+ qualType.IsLeftValue = isLValue;
+ return qualType;
+ }
+ else if( auto enumCaseDeclRef = declRef.as<EnumCaseDecl>() )
+ {
+ QualType qualType;
+ qualType.type = getType(enumCaseDeclRef);
+ qualType.IsLeftValue = false;
+ return qualType;
+ }
+ else if (auto typeAliasDeclRef = declRef.as<TypeDefDecl>())
+ {
+ auto type = getNamedType(session, typeAliasDeclRef);
+ *outTypeResult = type;
+ return QualType(getTypeType(type));
+ }
+ else if (auto aggTypeDeclRef = declRef.as<AggTypeDecl>())
+ {
+ auto type = DeclRefType::Create(session, aggTypeDeclRef);
+ *outTypeResult = type;
+ return QualType(getTypeType(type));
+ }
+ else if (auto simpleTypeDeclRef = declRef.as<SimpleTypeDecl>())
+ {
+ auto type = DeclRefType::Create(session, simpleTypeDeclRef);
+ *outTypeResult = type;
+ return QualType(getTypeType(type));
+ }
+ else if (auto genericDeclRef = declRef.as<GenericDecl>())
+ {
+ auto type = getGenericDeclRefType(session, genericDeclRef);
+ *outTypeResult = type;
+ return QualType(getTypeType(type));
+ }
+ else if (auto funcDeclRef = declRef.as<CallableDecl>())
+ {
+ auto type = getFuncType(session, funcDeclRef);
+ return QualType(type);
+ }
+ else if (auto constraintDeclRef = declRef.as<TypeConstraintDecl>())
+ {
+ // When we access a constraint or an inheritance decl (as a member),
+ // we are conceptually performing a "cast" to the given super-type,
+ // with the declaration showing that such a cast is legal.
+ auto type = GetSup(constraintDeclRef);
+ return QualType(type);
+ }
+ if( sink )
+ {
+ sink->diagnose(declRef, Diagnostics::unimplemented, "cannot form reference to this kind of declaration");
+ }
+ return QualType(session->getErrorType());
+ }
+
+ QualType getTypeForDeclRef(
+ Session* session,
+ DeclRef<Decl> declRef)
+ {
+ RefPtr<Type> typeResult;
+ return getTypeForDeclRef(session, nullptr, nullptr, declRef, &typeResult);
+ }
+
+ DeclRef<ExtensionDecl> ApplyExtensionToType(
+ SemanticsVisitor* semantics,
+ ExtensionDecl* extDecl,
+ RefPtr<Type> type)
+ {
+ if(!semantics)
+ return DeclRef<ExtensionDecl>();
+
+ return semantics->ApplyExtensionToType(extDecl, type);
+ }
+
+ RefPtr<GenericSubstitution> createDefaultSubsitutionsForGeneric(
+ Session* session,
+ GenericDecl* genericDecl,
+ RefPtr<Substitutions> outerSubst)
+ {
+ RefPtr<GenericSubstitution> genericSubst = new GenericSubstitution();
+ genericSubst->genericDecl = genericDecl;
+ genericSubst->outer = outerSubst;
+
+ for( auto mm : genericDecl->Members )
+ {
+ if( auto genericTypeParamDecl = as<GenericTypeParamDecl>(mm) )
+ {
+ genericSubst->args.add(DeclRefType::Create(session, DeclRef<Decl>(genericTypeParamDecl, outerSubst)));
+ }
+ else if( auto genericValueParamDecl = as<GenericValueParamDecl>(mm) )
+ {
+ genericSubst->args.add(new GenericParamIntVal(DeclRef<GenericValueParamDecl>(genericValueParamDecl, outerSubst)));
+ }
+ }
+
+ // create default substitution arguments for constraints
+ for (auto mm : genericDecl->Members)
+ {
+ if (auto genericTypeConstraintDecl = as<GenericTypeConstraintDecl>(mm))
+ {
+ RefPtr<DeclaredSubtypeWitness> witness = new DeclaredSubtypeWitness();
+ witness->declRef = DeclRef<Decl>(genericTypeConstraintDecl, outerSubst);
+ witness->sub = genericTypeConstraintDecl->sub.type;
+ witness->sup = genericTypeConstraintDecl->sup.type;
+ genericSubst->args.add(witness);
+ }
+ }
+
+ return genericSubst;
+ }
+
+ // Sometimes we need to refer to a declaration the way that it would be specialized
+ // inside the context where it is declared (e.g., with generic parameters filled in
+ // using their archetypes).
+ //
+ SubstitutionSet createDefaultSubstitutions(
+ Session* session,
+ Decl* decl,
+ SubstitutionSet outerSubstSet)
+ {
+ auto dd = decl->ParentDecl;
+ if( auto genericDecl = as<GenericDecl>(dd) )
+ {
+ // We don't want to specialize references to anything
+ // other than the "inner" declaration itself.
+ if(decl != genericDecl->inner)
+ return outerSubstSet;
+
+ RefPtr<GenericSubstitution> genericSubst = createDefaultSubsitutionsForGeneric(
+ session,
+ genericDecl,
+ outerSubstSet.substitutions);
+
+ return SubstitutionSet(genericSubst);
+ }
+
+ return outerSubstSet;
+ }
+
+ SubstitutionSet createDefaultSubstitutions(
+ Session* session,
+ Decl* decl)
+ {
+ SubstitutionSet subst;
+ if( auto parentDecl = decl->ParentDecl )
+ {
+ subst = createDefaultSubstitutions(session, parentDecl);
+ }
+ subst = createDefaultSubstitutions(session, decl, subst);
+ return subst;
+ }
+
+ void checkDecl(SemanticsVisitor* visitor, Decl* decl)
+ {
+ visitor->checkDecl(decl);
+ }
+
+ bool SemanticsVisitor::isDeclUsableAsStaticMember(
+ Decl* decl)
+ {
+ if(decl->HasModifier<HLSLStaticModifier>())
+ return true;
+
+ if(as<ConstructorDecl>(decl))
+ return true;
+
+ if(as<EnumCaseDecl>(decl))
+ return true;
+
+ if(as<AggTypeDeclBase>(decl))
+ return true;
+
+ if(as<SimpleTypeDecl>(decl))
+ return true;
+
+ return false;
+ }
+
+ bool SemanticsVisitor::isUsableAsStaticMember(
+ LookupResultItem const& item)
+ {
+ // There's a bit of a gotcha here, because a lookup result
+ // item might include "breadcrumbs" that indicate more steps
+ // along the lookup path. As a result it isn't always
+ // valid to just check whether the final decl is usable
+ // as a static member, because it might not even be a
+ // member of the thing we are trying to work with.
+ //
+
+ Decl* decl = item.declRef.getDecl();
+ for(auto bb = item.breadcrumbs; bb; bb = bb->next)
+ {
+ switch(bb->kind)
+ {
+ // In case lookup went through a `__transparent` member,
+ // we are interested in the static-ness of that transparent
+ // member, and *not* the static-ness of whatever was inside
+ // of it.
+ //
+ // TODO: This would need some work if we ever had
+ // transparent *type* members.
+ //
+ case LookupResultItem::Breadcrumb::Kind::Member:
+ decl = bb->declRef.getDecl();
+ break;
+
+ // TODO: Are there any other cases that need special-case
+ // handling here?
+
+ default:
+ break;
+ }
+ }
+
+ // Okay, we've found the declaration we should actually
+ // be checking, so lets validate that.
+
+ return isDeclUsableAsStaticMember(decl);
+ }
+
+ // 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)
+ {
+ if (decl->IsChecked(state)) return;
+ if (decl->checkState == DeclCheckState::CheckingHeader)
+ {
+ // We tried to reference the same declaration while checking it!
+ //
+ // TODO: we should ideally be tracking a "chain" of declarations
+ // being checked on the stack, so that we can report the full
+ // chain that leads from this declaration back to itself.
+ //
+ getSink()->diagnose(decl, Diagnostics::cyclicReference, decl);
+ 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.
+ //
+ // 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.
+ //
+ if (auto varDecl = as<VarDecl>(decl))
+ {
+ if (auto parenScope = as<ScopeDecl>(varDecl->ParentDecl))
+ {
+ // 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;
+ }
+ }
+
+ if (DeclCheckState::CheckingHeader > decl->checkState)
+ {
+ decl->SetCheckState(DeclCheckState::CheckingHeader);
+ }
+
+ // Check the modifiers on the declaration first, in case
+ // semantics of the body itself will depend on them.
+ checkModifiers(decl);
+
+ // Use visitor pattern to dispatch to correct case
+ dispatchDecl(decl);
+
+ if(state > decl->checkState)
+ {
+ decl->SetCheckState(state);
+ }
+ }
+
+ void SemanticsVisitor::EnusreAllDeclsRec(RefPtr<Decl> decl)
+ {
+ checkDecl(decl);
+ if (auto containerDecl = as<ContainerDecl>(decl))
+ {
+ for (auto m : containerDecl->Members)
+ {
+ EnusreAllDeclsRec(m);
+ }
+ }
+ }
+
+ void SemanticsVisitor::CheckVarDeclCommon(RefPtr<VarDeclBase> varDecl)
+ {
+ // A variable that didn't have an explicit type written must
+ // have its type inferred from the initial-value expression.
+ //
+ if(!varDecl->type.exp)
+ {
+ // In this case we need to perform all checking of the
+ // variable (including semantic checking of the initial-value
+ // expression) during the first phase of checking.
+
+ auto initExpr = varDecl->initExpr;
+ if(!initExpr)
+ {
+ getSink()->diagnose(varDecl, Diagnostics::varWithoutTypeMustHaveInitializer);
+ varDecl->type.type = getSession()->getErrorType();
+ }
+ else
+ {
+ initExpr = CheckExpr(initExpr);
+
+ // TODO: We might need some additional steps here to ensure
+ // that the type of the expression is one we are okay with
+ // inferring. E.g., if we ever decide that integer and floating-point
+ // literals have a distinct type from the standard int/float types,
+ // then we would need to "decay" a literal to an explicit type here.
+
+ varDecl->initExpr = initExpr;
+ varDecl->type.type = initExpr->type;
+ }
+
+ varDecl->SetCheckState(DeclCheckState::Checked);
+ }
+ else
+ {
+ if (function || checkingPhase == CheckingPhase::Header)
+ {
+ TypeExp typeExp = CheckUsableType(varDecl->type);
+ varDecl->type = typeExp;
+ if (varDecl->type.Equals(getSession()->getVoidType()))
+ {
+ getSink()->diagnose(varDecl, Diagnostics::invalidTypeVoid);
+ }
+ }
+
+ if (checkingPhase == CheckingPhase::Body)
+ {
+ if (auto initExpr = varDecl->initExpr)
+ {
+ initExpr = CheckTerm(initExpr);
+ 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(getCheckedState());
+ }
+
+ // Fill in default substitutions for the 'subtype' part of a type constraint decl
+ void SemanticsVisitor::CheckConstraintSubType(TypeExp& typeExp)
+ {
+ if (auto sharedTypeExpr = as<SharedTypeExpr>(typeExp.exp))
+ {
+ if (auto declRefType = as<DeclRefType>(sharedTypeExpr->base))
+ {
+ declRefType->declRef.substitutions = createDefaultSubstitutions(getSession(), declRefType->declRef.getDecl());
+
+ if (auto typetype = as<TypeType>(typeExp.exp->type))
+ typetype->type = declRefType;
+ }
+ }
+ }
+
+ void SemanticsVisitor::CheckGenericConstraintDecl(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);
+ }
+ }
+
+ void SemanticsVisitor::checkDecl(Decl* decl)
+ {
+ EnsureDecl(decl, checkingPhase == CheckingPhase::Header ? DeclCheckState::CheckedHeader : DeclCheckState::Checked);
+ }
+
+ void SemanticsVisitor::checkGenericDeclHeader(GenericDecl* genericDecl)
+ {
+ if (genericDecl->IsChecked(DeclCheckState::CheckedHeader))
+ return;
+ // check the parameters
+ for (auto m : genericDecl->Members)
+ {
+ if (auto typeParam = as<GenericTypeParamDecl>(m))
+ {
+ typeParam->initType = CheckProperType(typeParam->initType);
+ }
+ else if (auto valParam = as<GenericValueParamDecl>(m))
+ {
+ // TODO: some real checking here...
+ CheckVarDeclCommon(valParam);
+ }
+ else if (auto constraint = as<GenericTypeConstraintDecl>(m))
+ {
+ CheckGenericConstraintDecl(constraint);
+ }
+ }
+
+ 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)
+ {
+ 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);
+ base = TranslateTypeNode(base);
+ inheritanceDecl->base = base;
+
+ // For now we only allow inheritance from interfaces, so
+ // we will validate that the type expression names an interface
+
+ if(auto declRefType = as<DeclRefType>(base.type))
+ {
+ if(auto interfaceDeclRef = declRefType->declRef.as<InterfaceDecl>())
+ {
+ return;
+ }
+ }
+ else if(base.type.is<ErrorType>())
+ {
+ // If an error was already produced, don't emit a cascading error.
+ return;
+ }
+
+ // If type expression didn't name an interface, we'll emit an error here
+ // TODO: deal with the case of an error in the type expression (don't cascade)
+ 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*)
+ {
+ // These are only used in the stdlib, so no checking is needed for now
+ }
+
+ void SemanticsVisitor::visitGenericValueParamDecl(GenericValueParamDecl*)
+ {
+ // These are only used in the stdlib, so no checking is needed for now
+ }
+
+ void SemanticsVisitor::checkInterfaceConformancesRec(Decl* decl)
+ {
+ // Any user-defined type may have declared interface conformances,
+ // which we should check.
+ //
+ if( auto aggTypeDecl = as<AggTypeDecl>(decl) )
+ {
+ 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))
+ {
+ 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))
+ {
+ checkInterfaceConformancesRec(genericDecl->inner);
+ }
+ // 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))
+ {
+ for(auto member : containerDecl->Members)
+ {
+ checkInterfaceConformancesRec(member);
+ }
+ }
+ }
+
+ void SemanticsVisitor::visitModuleDecl(ModuleDecl* programNode)
+ {
+ // Try to register all the builtin decls
+ for (auto decl : programNode->Members)
+ {
+ auto inner = decl;
+ if (auto genericDecl = as<GenericDecl>(decl))
+ {
+ inner = genericDecl->inner;
+ }
+
+ if (auto builtinMod = inner->FindModifier<BuiltinTypeModifier>())
+ {
+ registerBuiltinDecl(getSession(), decl, builtinMod);
+ }
+ if (auto magicMod = inner->FindModifier<MagicTypeModifier>())
+ {
+ registerMagicDecl(getSession(), decl, magicMod);
+ }
+ }
+
+ // 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 extDecl = as<ExtensionDecl>(g->inner))
+ {
+ checkGenericDeclHeader(g);
+ registerExtension(extDecl);
+ }
+ }
+ // check user defined attribute classes first
+ for (auto decl : programNode->Members)
+ {
+ 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);
+ }
+ }
+ // check types
+ for (auto & s : programNode->getMembersOfType<TypeDefDecl>())
+ checkDecl(s.Ptr());
+
+ for (int pass = 0; pass < 2; pass++)
+ {
+ 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);
+ }
+
+ if (pass == 0)
+ {
+ checkInterfaceConformancesRec(programNode);
+ }
+ }
+ }
+
+ bool SemanticsVisitor::doesSignatureMatchRequirement(
+ DeclRef<CallableDecl> satisfyingMemberDeclRef,
+ DeclRef<CallableDecl> requiredMemberDeclRef,
+ RefPtr<WitnessTable> witnessTable)
+ {
+ if(satisfyingMemberDeclRef.getDecl()->HasModifier<MutatingAttribute>()
+ && !requiredMemberDeclRef.getDecl()->HasModifier<MutatingAttribute>())
+ {
+ // A `[mutating]` method can't satisfy a non-`[mutating]` requirement,
+ // but vice-versa is okay.
+ return false;
+ }
+
+ if(satisfyingMemberDeclRef.getDecl()->HasModifier<HLSLStaticModifier>()
+ != requiredMemberDeclRef.getDecl()->HasModifier<HLSLStaticModifier>())
+ {
+ // A `static` method can't satisfy a non-`static` requirement and vice versa.
+ return false;
+ }
+
+ // TODO: actually implement matching here. For now we'll
+ // just pretend that things are satisfied in order to make progress..
+ witnessTable->requirementDictionary.Add(
+ requiredMemberDeclRef.getDecl(),
+ RequirementWitness(satisfyingMemberDeclRef));
+ return true;
+ }
+
+ bool SemanticsVisitor::doesGenericSignatureMatchRequirement(
+ DeclRef<GenericDecl> genDecl,
+ DeclRef<GenericDecl> requirementGenDecl,
+ RefPtr<WitnessTable> witnessTable)
+ {
+ if (genDecl.getDecl()->Members.getCount() != requirementGenDecl.getDecl()->Members.getCount())
+ return false;
+ for (Index i = 0; i < genDecl.getDecl()->Members.getCount(); i++)
+ {
+ auto genMbr = genDecl.getDecl()->Members[i];
+ auto requiredGenMbr = genDecl.getDecl()->Members[i];
+ if (auto genTypeMbr = as<GenericTypeParamDecl>(genMbr))
+ {
+ if (auto requiredGenTypeMbr = as<GenericTypeParamDecl>(requiredGenMbr))
+ {
+ }
+ else
+ return false;
+ }
+ else if (auto genValMbr = as<GenericValueParamDecl>(genMbr))
+ {
+ if (auto requiredGenValMbr = as<GenericValueParamDecl>(requiredGenMbr))
+ {
+ if (!genValMbr->type->Equals(requiredGenValMbr->type))
+ return false;
+ }
+ else
+ return false;
+ }
+ else if (auto genTypeConstraintMbr = as<GenericTypeConstraintDecl>(genMbr))
+ {
+ if (auto requiredTypeConstraintMbr = as<GenericTypeConstraintDecl>(requiredGenMbr))
+ {
+ if (!genTypeConstraintMbr->sup->Equals(requiredTypeConstraintMbr->sup))
+ {
+ return false;
+ }
+ }
+ else
+ return false;
+ }
+ }
+
+ // TODO: this isn't right, because we need to specialize the
+ // declarations of the generics to a common set of substitutions,
+ // so that their types are comparable (e.g., foo<T> and foo<U>
+ // need to have substitutions applies so that they are both foo<X>,
+ // after which uses of the type X in their parameter lists can
+ // be compared).
+
+ return doesMemberSatisfyRequirement(
+ DeclRef<Decl>(genDecl.getDecl()->inner.Ptr(), genDecl.substitutions),
+ DeclRef<Decl>(requirementGenDecl.getDecl()->inner.Ptr(), requirementGenDecl.substitutions),
+ witnessTable);
+ }
+
+ bool SemanticsVisitor::doesTypeSatisfyAssociatedTypeRequirement(
+ RefPtr<Type> satisfyingType,
+ DeclRef<AssocTypeDecl> requiredAssociatedTypeDeclRef,
+ RefPtr<WitnessTable> witnessTable)
+ {
+ // We need to confirm that the chosen type `satisfyingType`,
+ // meets all the constraints placed on the associated type
+ // requirement `requiredAssociatedTypeDeclRef`.
+ //
+ // We will enumerate the type constraints placed on the
+ // associated type and see if they can be satisfied.
+ //
+ bool conformance = true;
+ for (auto requiredConstraintDeclRef : getMembersOfType<TypeConstraintDecl>(requiredAssociatedTypeDeclRef))
+ {
+ // Grab the type we expect to conform to from the constraint.
+ auto requiredSuperType = GetSup(requiredConstraintDeclRef);
+
+ // Perform a search for a witness to the subtype relationship.
+ auto witness = tryGetSubtypeWitness(satisfyingType, requiredSuperType);
+ if(witness)
+ {
+ // If a subtype witness was found, then the conformance
+ // appears to hold, and we can satisfy that requirement.
+ witnessTable->requirementDictionary.Add(requiredConstraintDeclRef, RequirementWitness(witness));
+ }
+ else
+ {
+ // If a witness couldn't be found, then the conformance
+ // seems like it will fail.
+ conformance = false;
+ }
+ }
+
+ // TODO: if any conformance check failed, we should probably include
+ // that in an error message produced about not satisfying the requirement.
+
+ if(conformance)
+ {
+ // If all the constraints were satisfied, then the chosen
+ // type can indeed satisfy the interface requirement.
+ witnessTable->requirementDictionary.Add(
+ requiredAssociatedTypeDeclRef.getDecl(),
+ RequirementWitness(satisfyingType));
+ }
+
+ return conformance;
+ }
+
+ bool SemanticsVisitor::doesMemberSatisfyRequirement(
+ DeclRef<Decl> memberDeclRef,
+ DeclRef<Decl> requiredMemberDeclRef,
+ RefPtr<WitnessTable> witnessTable)
+ {
+ // At a high level, we want to check that the
+ // `memberDecl` and the `requiredMemberDeclRef`
+ // have the same AST node class, and then also
+ // check that their signatures match.
+ //
+ // There are a bunch of detailed decisions that
+ // have to be made, though, because we might, e.g.,
+ // allow a function with more general parameter
+ // types to satisfy a requirement with more
+ // specific parameter types.
+ //
+ // If we ever allow for "property" declarations,
+ // then we would probably need to allow an
+ // ordinary field to satisfy a property requirement.
+ //
+ // An associated type requirement should be allowed
+ // to be satisfied by any type declaration:
+ // a typedef, a `struct`, etc.
+ //
+ if (auto memberFuncDecl = memberDeclRef.as<FuncDecl>())
+ {
+ if (auto requiredFuncDeclRef = requiredMemberDeclRef.as<FuncDecl>())
+ {
+ // Check signature match.
+ return doesSignatureMatchRequirement(
+ memberFuncDecl,
+ requiredFuncDeclRef,
+ witnessTable);
+ }
+ }
+ else if (auto memberInitDecl = memberDeclRef.as<ConstructorDecl>())
+ {
+ if (auto requiredInitDecl = requiredMemberDeclRef.as<ConstructorDecl>())
+ {
+ // Check signature match.
+ return doesSignatureMatchRequirement(
+ memberInitDecl,
+ requiredInitDecl,
+ witnessTable);
+ }
+ }
+ else if (auto genDecl = memberDeclRef.as<GenericDecl>())
+ {
+ // For a generic member, we will check if it can satisfy
+ // a generic requirement in the interface.
+ //
+ // TODO: we could also conceivably check that the generic
+ // could be *specialized* to satisfy the requirement,
+ // and then install a specialization of the generic into
+ // the witness table. Actually doing this would seem
+ // to require performing something akin to overload
+ // resolution as part of requirement satisfaction.
+ //
+ if (auto requiredGenDeclRef = requiredMemberDeclRef.as<GenericDecl>())
+ {
+ return doesGenericSignatureMatchRequirement(genDecl, requiredGenDeclRef, witnessTable);
+ }
+ }
+ else if (auto subAggTypeDeclRef = memberDeclRef.as<AggTypeDecl>())
+ {
+ if(auto requiredTypeDeclRef = requiredMemberDeclRef.as<AssocTypeDecl>())
+ {
+ checkDecl(subAggTypeDeclRef.getDecl());
+
+ auto satisfyingType = DeclRefType::Create(getSession(), subAggTypeDeclRef);
+ return doesTypeSatisfyAssociatedTypeRequirement(satisfyingType, requiredTypeDeclRef, witnessTable);
+ }
+ }
+ else if (auto typedefDeclRef = memberDeclRef.as<TypeDefDecl>())
+ {
+ // this is a type-def decl in an aggregate type
+ // check if the specified type satisfies the constraints defined by the associated type
+ if (auto requiredTypeDeclRef = requiredMemberDeclRef.as<AssocTypeDecl>())
+ {
+ checkDecl(typedefDeclRef.getDecl());
+
+ auto satisfyingType = getNamedType(getSession(), typedefDeclRef);
+ return doesTypeSatisfyAssociatedTypeRequirement(satisfyingType, requiredTypeDeclRef, witnessTable);
+ }
+ }
+ // Default: just assume that thing aren't being satisfied.
+ return false;
+ }
+
+ bool SemanticsVisitor::findWitnessForInterfaceRequirement(
+ ConformanceCheckingContext* context,
+ DeclRef<AggTypeDeclBase> typeDeclRef,
+ InheritanceDecl* inheritanceDecl,
+ DeclRef<InterfaceDecl> interfaceDeclRef,
+ DeclRef<Decl> requiredMemberDeclRef,
+ RefPtr<WitnessTable> witnessTable)
+ {
+ // The goal of this function is to find a suitable
+ // value to satisfy the requirement.
+ //
+ // The 99% case is that the requirement is a named member
+ // of the interface, and we need to search for a member
+ // with the same name in the type declaration and
+ // its (known) extensions.
+
+ // As a first pass, lets check if we already have a
+ // witness in the table for the requirement, so
+ // that we can bail out early.
+ //
+ if(witnessTable->requirementDictionary.ContainsKey(requiredMemberDeclRef.getDecl()))
+ {
+ return true;
+ }
+
+
+ // An important exception to the above is that an
+ // inheritance declaration in the interface is not going
+ // to be satisfied by an inheritance declaration in the
+ // conforming type, but rather by a full "witness table"
+ // full of the satisfying values for each requirement
+ // in the inherited-from interface.
+ //
+ if( auto requiredInheritanceDeclRef = requiredMemberDeclRef.as<InheritanceDecl>() )
+ {
+ // Recursively check that the type conforms
+ // to the inherited interface.
+ //
+ // TODO: we *really* need a linearization step here!!!!
+
+ RefPtr<WitnessTable> satisfyingWitnessTable = checkConformanceToType(
+ context,
+ typeDeclRef,
+ requiredInheritanceDeclRef.getDecl(),
+ getBaseType(requiredInheritanceDeclRef));
+
+ if(!satisfyingWitnessTable)
+ return false;
+
+ witnessTable->requirementDictionary.Add(
+ requiredInheritanceDeclRef.getDecl(),
+ RequirementWitness(satisfyingWitnessTable));
+ return true;
+ }
+
+ // We will look up members with the same name,
+ // since only same-name members will be able to
+ // satisfy the requirement.
+ //
+ // TODO: this won't work right now for members that
+ // don't have names, which right now includes
+ // initializers/constructors.
+ Name* name = requiredMemberDeclRef.GetName();
+
+ // We are basically looking up members of the
+ // given type, but we need to be a bit careful.
+ // We *cannot* perfom lookup "through" inheritance
+ // declarations for this or other interfaces,
+ // since that would let us satisfy a requirement
+ // with itself.
+ //
+ // There's also an interesting question of whether
+ // we can/should support innterface requirements
+ // being satisfied via `__transparent` members.
+ // This seems like a "clever" idea rather than
+ // a useful one, and IR generation would
+ // need to construct real IR to trampoline over
+ // to the implementation.
+ //
+ // The final case that can't be reduced to just
+ // "a directly declared member with the same name"
+ // is the case where the type inherits a member
+ // that can satisfy the requirement from a base type.
+ // We are ignoring implementation inheritance for
+ // now, so we won't worry about this.
+
+ // Make sure that by-name lookup is possible.
+ buildMemberDictionary(typeDeclRef.getDecl());
+ auto lookupResult = lookUpLocal(getSession(), this, name, typeDeclRef);
+
+ if (!lookupResult.isValid())
+ {
+ getSink()->diagnose(inheritanceDecl, Diagnostics::typeDoesntImplementInterfaceRequirement, typeDeclRef, requiredMemberDeclRef);
+ return false;
+ }
+
+ // Iterate over the members and look for one that matches
+ // the expected signature for the requirement.
+ for (auto member : lookupResult)
+ {
+ if (doesMemberSatisfyRequirement(member.declRef, requiredMemberDeclRef, witnessTable))
+ return true;
+ }
+
+ // No suitable member found, although there were candidates.
+ //
+ // TODO: Eventually we might want something akin to the current
+ // overload resolution logic, where we keep track of a list
+ // of "candidates" for satisfaction of the requirement,
+ // and if nothing is found we print the candidates
+
+ getSink()->diagnose(inheritanceDecl, Diagnostics::typeDoesntImplementInterfaceRequirement, typeDeclRef, requiredMemberDeclRef);
+ return false;
+ }
+
+ RefPtr<WitnessTable> SemanticsVisitor::checkInterfaceConformance(
+ ConformanceCheckingContext* context,
+ DeclRef<AggTypeDeclBase> typeDeclRef,
+ InheritanceDecl* inheritanceDecl,
+ DeclRef<InterfaceDecl> interfaceDeclRef)
+ {
+ // Has somebody already checked this conformance,
+ // and/or is in the middle of checking it?
+ RefPtr<WitnessTable> witnessTable;
+ if(context->mapInterfaceToWitnessTable.TryGetValue(interfaceDeclRef, witnessTable))
+ return witnessTable;
+
+ // We need to check the declaration of the interface
+ // before we can check that we conform to it.
+ checkDecl(interfaceDeclRef.getDecl());
+
+ // We will construct the witness table, and register it
+ // *before* we go about checking fine-grained requirements,
+ // in order to short-circuit any potential for infinite recursion.
+
+ // Note: we will re-use the witnes table attached to the inheritance decl,
+ // if there is one. This catches cases where semantic checking might
+ // have synthesized some of the conformance witnesses for us.
+ //
+ witnessTable = inheritanceDecl->witnessTable;
+ if(!witnessTable)
+ {
+ witnessTable = new WitnessTable();
+ }
+ context->mapInterfaceToWitnessTable.Add(interfaceDeclRef, witnessTable);
+
+ bool result = true;
+
+ // TODO: If we ever allow for implementation inheritance,
+ // then we will need to consider the case where a type
+ // declares that it conforms to an interface, but one of
+ // its (non-interface) base types already conforms to
+ // that interface, so that all of the requirements are
+ // already satisfied with inherited implementations...
+ for(auto requiredMemberDeclRef : getMembers(interfaceDeclRef))
+ {
+ auto requirementSatisfied = findWitnessForInterfaceRequirement(
+ context,
+ typeDeclRef,
+ inheritanceDecl,
+ interfaceDeclRef,
+ requiredMemberDeclRef,
+ witnessTable);
+
+ result = result && requirementSatisfied;
+ }
+
+ // Extensions that apply to the interface type can create new conformances
+ // for the concrete types that inherit from the interface.
+ //
+ // These new conformances should not be able to introduce new *requirements*
+ // for an implementing interface (although they currently can), but we
+ // still need to go through this logic to find the appropriate value
+ // that will satisfy the requirement in these cases, and also to put
+ // the required entry into the witness table for the interface itself.
+ //
+ // TODO: This logic is a bit slippery, and we need to figure out what
+ // it means in the context of separate compilation. If module A defines
+ // an interface IA, module B defines a type C that conforms to IA, and then
+ // module C defines an extension that makes IA conform to IC, then it is
+ // unreasonable to expect the {B:IA} witness table to contain an entry
+ // corresponding to {IA:IC}.
+ //
+ // The simple answer then would be that the {IA:IC} conformance should be
+ // fixed, with a single witness table for {IA:IC}, but then what should
+ // happen in B explicitly conformed to IC already?
+ //
+ // For now we will just walk through the extensions that are known at
+ // the time we are compiling and handle those, and punt on the larger issue
+ // for abit longer.
+ for(auto candidateExt = interfaceDeclRef.getDecl()->candidateExtensions; candidateExt; candidateExt = candidateExt->nextCandidateExtension)
+ {
+ // We need to apply the extension to the interface type that our
+ // concrete type is inheriting from.
+ //
+ // TODO: need to decide if a this-type substitution is needed here.
+ // It probably it.
+ RefPtr<Type> targetType = DeclRefType::Create(
+ getSession(),
+ interfaceDeclRef);
+ auto extDeclRef = ApplyExtensionToType(candidateExt, targetType);
+ if(!extDeclRef)
+ continue;
+
+ // Only inheritance clauses from the extension matter right now.
+ for(auto requiredInheritanceDeclRef : getMembersOfType<InheritanceDecl>(extDeclRef))
+ {
+ auto requirementSatisfied = findWitnessForInterfaceRequirement(
+ context,
+ typeDeclRef,
+ inheritanceDecl,
+ interfaceDeclRef,
+ requiredInheritanceDeclRef,
+ witnessTable);
+
+ result = result && requirementSatisfied;
+ }
+ }
+
+ // If we failed to satisfy any requirements along the way,
+ // then we don't actually want to keep the witness table
+ // we've been constructing, because the whole thing was a failure.
+ if(!result)
+ {
+ return nullptr;
+ }
+
+ return witnessTable;
+ }
+
+ RefPtr<WitnessTable> SemanticsVisitor::checkConformanceToType(
+ ConformanceCheckingContext* context,
+ DeclRef<AggTypeDeclBase> typeDeclRef,
+ InheritanceDecl* inheritanceDecl,
+ Type* baseType)
+ {
+ if (auto baseDeclRefType = as<DeclRefType>(baseType))
+ {
+ auto baseTypeDeclRef = baseDeclRefType->declRef;
+ if (auto baseInterfaceDeclRef = baseTypeDeclRef.as<InterfaceDecl>())
+ {
+ // The type is stating that it conforms to an interface.
+ // We need to check that it provides all of the members
+ // required by that interface.
+ return checkInterfaceConformance(
+ context,
+ typeDeclRef,
+ inheritanceDecl,
+ baseInterfaceDeclRef);
+ }
+ }
+
+ getSink()->diagnose(inheritanceDecl, Diagnostics::unimplemented, "type not supported for inheritance");
+ return nullptr;
+ }
+
+ bool SemanticsVisitor::checkConformance(
+ DeclRef<AggTypeDeclBase> declRef,
+ InheritanceDecl* inheritanceDecl)
+ {
+ declRef = createDefaultSubstitutionsIfNeeded(getSession(), declRef).as<AggTypeDeclBase>();
+
+ // Don't check conformances for abstract types that
+ // are being used to express *required* conformances.
+ if (auto assocTypeDeclRef = declRef.as<AssocTypeDecl>())
+ {
+ // An associated type declaration represents a requirement
+ // in an outer interface declaration, and its members
+ // (type constraints) represent additional requirements.
+ return true;
+ }
+ else if (auto interfaceDeclRef = declRef.as<InterfaceDecl>())
+ {
+ // HACK: Our semantics as they stand today are that an
+ // `extension` of an interface that adds a new inheritance
+ // clause acts *as if* that inheritnace clause had been
+ // attached to the original `interface` decl: that is,
+ // it adds additional requirements.
+ //
+ // This is *not* a reasonable semantic to keep long-term,
+ // but it is required for some of our current example
+ // code to work.
+ return true;
+ }
+
+
+ // Look at the type being inherited from, and validate
+ // appropriately.
+ auto baseType = inheritanceDecl->base.type;
+
+ ConformanceCheckingContext context;
+ RefPtr<WitnessTable> witnessTable = checkConformanceToType(&context, declRef, inheritanceDecl, baseType);
+ if(!witnessTable)
+ return false;
+
+ inheritanceDecl->witnessTable = witnessTable;
+ return true;
+ }
+
+ void SemanticsVisitor::checkExtensionConformance(ExtensionDecl* decl)
+ {
+ if (auto targetDeclRefType = as<DeclRefType>(decl->targetType))
+ {
+ if (auto aggTypeDeclRef = targetDeclRefType->declRef.as<AggTypeDecl>())
+ {
+ for (auto inheritanceDecl : decl->getMembersOfType<InheritanceDecl>())
+ {
+ checkConformance(aggTypeDeclRef, inheritanceDecl);
+ }
+ }
+ }
+ }
+
+ void SemanticsVisitor::checkAggTypeConformance(AggTypeDecl* decl)
+ {
+ // After we've checked members, we need to go through
+ // any inheritance clauses on the type itself, and
+ // confirm that the type actually provides whatever
+ // those clauses require.
+
+ if (auto interfaceDecl = as<InterfaceDecl>(decl))
+ {
+ // Don't check that an interface conforms to the
+ // things it inherits from.
+ }
+ else if (auto assocTypeDecl = as<AssocTypeDecl>(decl))
+ {
+ // Don't check that an associated type decl conforms to the
+ // things it inherits from.
+ }
+ else
+ {
+ // For non-interface types we need to check conformance.
+ //
+ // TODO: Need to figure out what this should do for
+ // `abstract` types if we ever add them. Should they
+ // be required to implement all interface requirements,
+ // just with `abstract` methods that replicate things?
+ // (That's what C# does).
+ for (auto inheritanceDecl : decl->getMembersOfType<InheritanceDecl>())
+ {
+ checkConformance(makeDeclRef(decl), inheritanceDecl);
+ }
+ }
+ }
+
+ void SemanticsVisitor::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);
+
+ // Now check all of the member declarations.
+ for (auto member : decl->Members)
+ {
+ checkDecl(member);
+ }
+ decl->SetCheckState(getCheckedState());
+ }
+
+ bool SemanticsVisitor::isIntegerBaseType(BaseType baseType)
+ {
+ switch(baseType)
+ {
+ default:
+ return false;
+
+ case BaseType::Int8:
+ case BaseType::Int16:
+ case BaseType::Int:
+ case BaseType::Int64:
+ case BaseType::UInt8:
+ case BaseType::UInt16:
+ case BaseType::UInt:
+ case BaseType::UInt64:
+ return true;
+ }
+ }
+
+ void SemanticsVisitor::validateEnumTagType(Type* type, SourceLoc const& loc)
+ {
+ if(auto basicType = as<BasicExpressionType>(type))
+ {
+ // Allow the built-in integer types.
+ if(isIntegerBaseType(basicType->baseType))
+ return;
+
+ // By default, don't allow other types to be used
+ // as an `enum` tag type.
+ }
+
+ getSink()->diagnose(loc, Diagnostics::invalidEnumTagType, type);
+ }
+
+ void SemanticsVisitor::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 the type being inherited from.
+ auto superType = inheritanceDecl->base.type;
+
+ 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>())
+ {
+ // 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)
+ {
+ 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>())
+ {
+ if(auto enumTypeTypeInterfaceDecl = as<InterfaceDecl>(enumTypeTypeDeclRefType->declRef.getDecl()))
+ {
+ for(auto memberDecl : enumTypeTypeInterfaceDecl->Members)
+ {
+ if(memberDecl->getName() == tagAssociatedTypeName)
+ {
+ tagAssociatedTypeDecl = memberDecl;
+ break;
+ }
+ }
+ }
+ }
+ 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));
+
+ // 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.
+
+ enumConformanceDecl->SetCheckState(DeclCheckState::Checked);
+ }
+ }
+ else if( checkingPhase == CheckingPhase::Body )
+ {
+ auto enumType = DeclRefType::Create(
+ getSession(),
+ makeDeclRef(decl));
+
+ auto tagType = decl->tagType;
+
+ // 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;
+
+ checkDecl(caseDecl);
+ }
+
+ // 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)
+ {
+ // 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)
+ {
+ if(auto constIntVal = as<ConstantIntVal>(explicitTagVal))
+ {
+ defaultTag = constIntVal->value;
+ }
+ else
+ {
+ // TODO: need to handle other possibilities here
+ getSink()->diagnose(explicitTagValExpr, Diagnostics::unexpectedEnumTagExpr);
+ }
+ }
+ 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.
+ }
+ }
+ 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;
+ }
+
+ // 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)
+ {
+ // 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;
+
+ // 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);
+ }
+ }
+ decl->SetCheckState(getCheckedState());
+ }
+
+ void SemanticsVisitor::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);
+
+ // We want to enforce that this is an integer constant
+ // expression, but we don't actually care to retain
+ // the value.
+ CheckIntegerConstantExpression(initExpr);
+
+ decl->tagExpr = initExpr;
+ }
+ }
+
+ decl->SetCheckState(getCheckedState());
+ }
+
+ void SemanticsVisitor::visitDeclGroup(DeclGroup* declGroup)
+ {
+ for (auto decl : declGroup->decls)
+ {
+ dispatchDecl(decl);
+ }
+ }
+
+ void SemanticsVisitor::visitTypeDefDecl(TypeDefDecl* decl)
+ {
+ if (decl->IsChecked(getCheckedState())) return;
+ if (checkingPhase == CheckingPhase::Header)
+ {
+ decl->type = CheckProperType(decl->type);
+ }
+ decl->SetCheckState(getCheckedState());
+ }
+
+ void SemanticsVisitor::visitGlobalGenericParamDecl(GlobalGenericParamDecl* decl)
+ {
+ if (decl->IsChecked(getCheckedState())) return;
+ if (checkingPhase == CheckingPhase::Header)
+ {
+ 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)
+ {
+ checkDecl(member);
+ }
+ }
+ decl->SetCheckState(getCheckedState());
+ }
+
+ void SemanticsVisitor::visitAssocTypeDecl(AssocTypeDecl* 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());
+ }
+
+ void SemanticsVisitor::visitFuncDecl(FuncDecl* functionNode)
+ {
+ if (functionNode->IsChecked(getCheckedState()))
+ return;
+
+ if (checkingPhase == CheckingPhase::Header)
+ {
+ VisitFunctionDeclaration(functionNode);
+ }
+ // TODO: This should really only set "checked header"
+ functionNode->SetCheckState(getCheckedState());
+
+ if (checkingPhase == CheckingPhase::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;
+ }
+ }
+ }
+
+ void SemanticsVisitor::getGenericParams(
+ GenericDecl* decl,
+ List<Decl*>& outParams,
+ List<GenericTypeConstraintDecl*> outConstraints)
+ {
+ for (auto dd : decl->Members)
+ {
+ if (dd == decl->inner)
+ continue;
+
+ if (auto typeParamDecl = as<GenericTypeParamDecl>(dd))
+ outParams.add(typeParamDecl);
+ else if (auto valueParamDecl = as<GenericValueParamDecl>(dd))
+ outParams.add(valueParamDecl);
+ else if (auto constraintDecl = as<GenericTypeConstraintDecl>(dd))
+ outConstraints.add(constraintDecl);
+ }
+ }
+
+ bool SemanticsVisitor::doGenericSignaturesMatch(
+ GenericDecl* fst,
+ GenericDecl* snd)
+ {
+ // First we'll extract the parameters and constraints
+ // in each generic signature. We will consider parameters
+ // and constraints separately so that we are independent
+ // of the order in which constraints are given (that is,
+ // a constraint like `<T : IFoo>` would be considered
+ // the same as `<T>` with a later `where T : IFoo`.
+
+ List<Decl*> fstParams;
+ List<GenericTypeConstraintDecl*> fstConstraints;
+ getGenericParams(fst, fstParams, fstConstraints);
+
+ List<Decl*> sndParams;
+ List<GenericTypeConstraintDecl*> sndConstraints;
+ getGenericParams(snd, sndParams, sndConstraints);
+
+ // For there to be any hope of a match, the
+ // two need to have the same number of parameters.
+ Index paramCount = fstParams.getCount();
+ if (paramCount != sndParams.getCount())
+ return false;
+
+ // Now we'll walk through the parameters.
+ for (Index pp = 0; pp < paramCount; ++pp)
+ {
+ Decl* fstParam = fstParams[pp];
+ Decl* sndParam = sndParams[pp];
+
+ if (auto fstTypeParam = as<GenericTypeParamDecl>(fstParam))
+ {
+ if (auto sndTypeParam = as<GenericTypeParamDecl>(sndParam))
+ {
+ // TODO: is there any validation that needs to be performed here?
+ }
+ else
+ {
+ // Type and non-type parameters can't match.
+ return false;
+ }
+ }
+ else if (auto fstValueParam = as<GenericValueParamDecl>(fstParam))
+ {
+ if (auto sndValueParam = as<GenericValueParamDecl>(sndParam))
+ {
+ // Need to check that the parameters have the same type.
+ //
+ // Note: We are assuming here that the type of a value
+ // parameter cannot be dependent on any of the type
+ // parameters in the same signature. This is a reasonable
+ // assumption for now, but could get thorny down the road.
+ if (!fstValueParam->getType()->Equals(sndValueParam->getType()))
+ {
+ // Type mismatch.
+ return false;
+ }
+
+ // TODO: This is not the right place to check on default
+ // values for the parameter, because they won't affect
+ // the signature, but we should make sure to do validation
+ // later on (e.g., that only one declaration can/should
+ // be allowed to provide a default).
+ }
+ else
+ {
+ // Value and non-value parameters can't match.
+ return false;
+ }
+ }
+ }
+
+ // If we got this far, then it means the parameter signatures *seem*
+ // to match up all right, but now we need to check that the constraints
+ // placed on those parameters are also consistent.
+ //
+ // For now I'm going to assume/require that all declarations must
+ // declare the signature in a way that matches exactly.
+ Index constraintCount = fstConstraints.getCount();
+ if(constraintCount != sndConstraints.getCount())
+ return false;
+
+ for (Index cc = 0; cc < constraintCount; ++cc)
+ {
+ //auto fstConstraint = fstConstraints[cc];
+ //auto sndConstraint = sndConstraints[cc];
+
+ // TODO: the challenge here is that the
+ // constraints are going to be expressed
+ // in terms of the parameters, which means
+ // we need to be doing substitution here.
+ }
+
+ // HACK: okay, we'll just assume things match for now.
+ return true;
+ }
+
+ bool SemanticsVisitor::doFunctionSignaturesMatch(
+ DeclRef<FuncDecl> fst,
+ DeclRef<FuncDecl> snd)
+ {
+
+ // TODO(tfoley): This copies the parameter array, which is bad for performance.
+ auto fstParams = GetParameters(fst).ToArray();
+ auto sndParams = GetParameters(snd).ToArray();
+
+ // If the functions have different numbers of parameters, then
+ // their signatures trivially don't match.
+ auto fstParamCount = fstParams.getCount();
+ auto sndParamCount = sndParams.getCount();
+ if (fstParamCount != sndParamCount)
+ return false;
+
+ for (Index ii = 0; ii < fstParamCount; ++ii)
+ {
+ auto fstParam = fstParams[ii];
+ auto sndParam = sndParams[ii];
+
+ // If a given parameter type doesn't match, then signatures don't match
+ if (!GetType(fstParam)->Equals(GetType(sndParam)))
+ return false;
+
+ // If one parameter is `out` and the other isn't, then they don't match
+ //
+ // Note(tfoley): we don't consider `out` and `inout` as distinct here,
+ // because there is no way for overload resolution to pick between them.
+ if (fstParam.getDecl()->HasModifier<OutModifier>() != sndParam.getDecl()->HasModifier<OutModifier>())
+ return false;
+
+ // If one parameter is `ref` and the other isn't, then they don't match.
+ //
+ if(fstParam.getDecl()->HasModifier<RefModifier>() != sndParam.getDecl()->HasModifier<RefModifier>())
+ return false;
+ }
+
+ // Note(tfoley): return type doesn't enter into it, because we can't take
+ // calling context into account during overload resolution.
+
+ return true;
+ }
+
+ RefPtr<GenericSubstitution> SemanticsVisitor::createDummySubstitutions(
+ GenericDecl* genericDecl)
+ {
+ RefPtr<GenericSubstitution> subst = new GenericSubstitution();
+ subst->genericDecl = genericDecl;
+ for (auto dd : genericDecl->Members)
+ {
+ if (dd == genericDecl->inner)
+ continue;
+
+ if (auto typeParam = as<GenericTypeParamDecl>(dd))
+ {
+ auto type = DeclRefType::Create(getSession(),
+ makeDeclRef(typeParam));
+ subst->args.add(type);
+ }
+ else if (auto valueParam = as<GenericValueParamDecl>(dd))
+ {
+ auto val = new GenericParamIntVal(
+ makeDeclRef(valueParam));
+ subst->args.add(val);
+ }
+ // TODO: need to handle constraints here?
+ }
+ return subst;
+ }
+
+ void SemanticsVisitor::ValidateFunctionRedeclaration(FuncDecl* funcDecl)
+ {
+ auto parentDecl = funcDecl->ParentDecl;
+ SLANG_ASSERT(parentDecl);
+ if (!parentDecl) return;
+
+ Decl* childDecl = funcDecl;
+
+ // If this is a generic function (that is, its parent
+ // declaration is a generic), then we need to look
+ // for sibling declarations of the parent.
+ auto genericDecl = as<GenericDecl>(parentDecl);
+ if (genericDecl)
+ {
+ parentDecl = genericDecl->ParentDecl;
+ childDecl = genericDecl;
+ }
+
+ // Look at previously-declared functions with the same name,
+ // in the same container
+ //
+ // Note: there is an assumption here that declarations that
+ // occur earlier in the program text will be *later* in
+ // the linked list of declarations with the same name.
+ // We are also assuming/requiring that the check here is
+ // symmetric, in that it is okay to test (A,B) or (B,A),
+ // and there is no need to test both.
+ //
+ buildMemberDictionary(parentDecl);
+ for (auto pp = childDecl->nextInContainerWithSameName; pp; pp = pp->nextInContainerWithSameName)
+ {
+ auto prevDecl = pp;
+
+ // Look through generics to the declaration underneath
+ auto prevGenericDecl = as<GenericDecl>(prevDecl);
+ if (prevGenericDecl)
+ prevDecl = prevGenericDecl->inner.Ptr();
+
+ // We only care about previously-declared functions
+ // Note(tfoley): although we should really error out if the
+ // name is already in use for something else, like a variable...
+ auto prevFuncDecl = as<FuncDecl>(prevDecl);
+ if (!prevFuncDecl)
+ continue;
+
+ // If one declaration is a prefix/postfix operator, and the
+ // other is not a matching operator, then don't consider these
+ // to be re-declarations.
+ //
+ // Note(tfoley): Any attempt to call such an operator using
+ // ordinary function-call syntax (if we decided to allow it)
+ // would be ambiguous in such a case, of course.
+ //
+ if (funcDecl->HasModifier<PrefixModifier>() != prevDecl->HasModifier<PrefixModifier>())
+ continue;
+ if (funcDecl->HasModifier<PostfixModifier>() != prevDecl->HasModifier<PostfixModifier>())
+ continue;
+
+ // If one is generic and the other isn't, then there is no match.
+ if ((genericDecl != nullptr) != (prevGenericDecl != nullptr))
+ continue;
+
+ // We are going to be comparing the signatures of the
+ // two functions, but if they are *generic* functions
+ // then we will need to compare them with consistent
+ // specializations in place.
+ //
+ // We'll go ahead and create some (unspecialized) declaration
+ // references here, just to be prepared.
+ DeclRef<FuncDecl> funcDeclRef(funcDecl, nullptr);
+ DeclRef<FuncDecl> prevFuncDeclRef(prevFuncDecl, nullptr);
+
+ // If we are working with generic functions, then we need to
+ // consider if their generic signatures match.
+ if (genericDecl)
+ {
+ SLANG_ASSERT(prevGenericDecl); // already checked above
+ if (!doGenericSignaturesMatch(genericDecl, prevGenericDecl))
+ continue;
+
+ // Now we need specialize the declaration references
+ // consistently, so that we can compare.
+ //
+ // First we create a "dummy" set of substitutions that
+ // just reference the parameters of the first generic.
+ auto subst = createDummySubstitutions(genericDecl);
+ //
+ // Then we use those parameters to specialize the *other*
+ // generic.
+ //
+ subst->genericDecl = prevGenericDecl;
+ prevFuncDeclRef.substitutions.substitutions = subst;
+ //
+ // One way to think about it is that if we have these
+ // declarations (ignore the name differences...):
+ //
+ // // prevFuncDecl:
+ // void foo1<T>(T x);
+ //
+ // // funcDecl:
+ // void foo2<U>(U x);
+ //
+ // Then we will compare `foo2` against `foo1<U>`.
+ }
+
+ // If the parameter signatures don't match, then don't worry
+ if (!doFunctionSignaturesMatch(funcDeclRef, prevFuncDeclRef))
+ continue;
+
+ // If we get this far, then we've got two declarations in the same
+ // scope, with the same name and signature, so they appear
+ // to be redeclarations.
+ //
+ // We will track that redeclaration occured, so that we can
+ // take it into account for overload resolution.
+ //
+ // A huge complication that we'll need to deal with is that
+ // multiple declarations might introduce default values for
+ // (different) parameters, and we might need to merge across
+ // all of them (which could get complicated if defaults for
+ // parameters can reference earlier parameters).
+
+ // If the previous declaration wasn't already recorded
+ // as being part of a redeclaration family, then make
+ // it the primary declaration of a new family.
+ if (!prevFuncDecl->primaryDecl)
+ {
+ prevFuncDecl->primaryDecl = prevFuncDecl;
+ }
+
+ // The new declaration will belong to the family of
+ // the previous one, and so it will share the same
+ // primary declaration.
+ funcDecl->primaryDecl = prevFuncDecl->primaryDecl;
+ funcDecl->nextDecl = nullptr;
+
+ // Next we want to chain the new declaration onto
+ // the linked list of redeclarations.
+ auto link = &prevFuncDecl->nextDecl;
+ while (*link)
+ link = &(*link)->nextDecl;
+ *link = funcDecl;
+
+ // Now that we've added things to a group of redeclarations,
+ // we can do some additional validation.
+
+ // First, we will ensure that the return types match
+ // between the declarations, so that they are truly
+ // interchangeable.
+ //
+ // Note(tfoley): If we ever decide to add a beefier type
+ // system to Slang, we might allow overloads like this,
+ // so long as the desired result type can be disambiguated
+ // based on context at the call type. In that case we would
+ // consider result types earlier, as part of the signature
+ // matching step.
+ //
+ auto resultType = GetResultType(funcDeclRef);
+ auto prevResultType = GetResultType(prevFuncDeclRef);
+ if (!resultType->Equals(prevResultType))
+ {
+ // Bad redeclaration
+ getSink()->diagnose(funcDecl, Diagnostics::functionRedeclarationWithDifferentReturnType, funcDecl->getName(), resultType, prevResultType);
+ getSink()->diagnose(prevFuncDecl, Diagnostics::seePreviousDeclarationOf, funcDecl->getName());
+
+ // Don't bother emitting other errors at this point
+ break;
+ }
+
+ // Note(tfoley): several of the following checks should
+ // really be looping over all the previous declarations
+ // in the same group, and not just the one previous
+ // declaration we found just now.
+
+ // TODO: Enforce that the new declaration had better
+ // not specify a default value for any parameter that
+ // already had a default value in a prior declaration.
+
+ // We are going to want to enforce that we cannot have
+ // two declarations of a function both specify bodies.
+ // Before we make that check, however, we need to deal
+ // with the case where the two function declarations
+ // might represent different target-specific versions
+ // of a function.
+ //
+ // TODO: if the two declarations are specialized for
+ // different targets, then skip the body checks below.
+
+ // If both of the declarations have a body, then there
+ // is trouble, because we wouldn't know which one to
+ // use during code generation.
+ if (funcDecl->Body && prevFuncDecl->Body)
+ {
+ // Redefinition
+ getSink()->diagnose(funcDecl, Diagnostics::functionRedefinition, funcDecl->getName());
+ getSink()->diagnose(prevFuncDecl, Diagnostics::seePreviousDefinitionOf, funcDecl->getName());
+
+ // Don't bother emitting other errors
+ break;
+ }
+
+ // At this point we've processed the redeclaration and
+ // put it into a group, so there is no reason to keep
+ // looping and looking at prior declarations.
+ return;
+ }
+ }
+
+ void SemanticsVisitor::visitScopeDecl(ScopeDecl*)
+ {
+ // Nothing to do
+ }
+
+ void SemanticsVisitor::visitParamDecl(ParamDecl* paramDecl)
+ {
+ // TODO: This logic should be shared with the other cases of
+ // variable declarations. The main reason I am not doing it
+ // yet is that we use a `ParamDecl` with a null type as a
+ // special case in attribute declarations, and that could
+ // trip up the ordinary variable checks.
+
+ auto typeExpr = paramDecl->type;
+ if(typeExpr.exp)
+ {
+ typeExpr = CheckUsableType(typeExpr);
+ paramDecl->type = typeExpr;
+ }
+
+ paramDecl->SetCheckState(DeclCheckState::CheckedHeader);
+
+ // The "initializer" expression for a parameter represents
+ // a default argument value to use if an explicit one is
+ // not supplied.
+ if(auto initExpr = paramDecl->initExpr)
+ {
+ // We must check the expression and coerce it to the
+ // actual type of the parameter.
+ //
+ initExpr = CheckExpr(initExpr);
+ initExpr = coerce(typeExpr.type, initExpr);
+ paramDecl->initExpr = initExpr;
+
+ // TODO: a default argument expression needs to
+ // conform to other constraints to be valid.
+ // For example, it should not be allowed to refer
+ // to other parameters of the same function (or maybe
+ // only the parameters to its left...).
+
+ // A default argument value should not be allowed on an
+ // `out` or `inout` parameter.
+ //
+ // TODO: we could relax this by requiring the expression
+ // to yield an lvalue, but that seems like a feature
+ // with limited practical utility (and an easy source
+ // of confusing behavior).
+ //
+ // Note: the `InOutModifier` class inherits from `OutModifier`,
+ // so we only need to check for the base case.
+ //
+ if(paramDecl->FindModifier<OutModifier>())
+ {
+ getSink()->diagnose(initExpr, Diagnostics::outputParameterCannotHaveDefaultValue);
+ }
+ }
+
+ paramDecl->SetCheckState(DeclCheckState::Checked);
+ }
+
+ void SemanticsVisitor::VisitFunctionDeclaration(FuncDecl *functionNode)
+ {
+ if (functionNode->IsChecked(DeclCheckState::CheckedHeader)) return;
+ functionNode->SetCheckState(DeclCheckState::CheckingHeader);
+ auto oldFunc = this->function;
+ this->function = functionNode;
+
+ auto resultType = functionNode->ReturnType;
+ if(resultType.exp)
+ {
+ resultType = CheckProperType(functionNode->ReturnType);
+ }
+ else
+ {
+ resultType = TypeExp(getSession()->getVoidType());
+ }
+ functionNode->ReturnType = resultType;
+
+
+ HashSet<Name*> paraNames;
+ for (auto & para : functionNode->GetParameters())
+ {
+ EnsureDecl(para, DeclCheckState::CheckedHeader);
+
+ if (paraNames.Contains(para->getName()))
+ {
+ getSink()->diagnose(para, Diagnostics::parameterAlreadyDefined, para->getName());
+ }
+ 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);
+ }
+
+ IntegerLiteralValue SemanticsVisitor::GetMinBound(RefPtr<IntVal> val)
+ {
+ if (auto constantVal = as<ConstantIntVal>(val))
+ return constantVal->value;
+
+ // TODO(tfoley): Need to track intervals so that this isn't just a lie...
+ return 1;
+ }
+
+ void SemanticsVisitor::maybeInferArraySizeForVariable(VarDeclBase* varDecl)
+ {
+ // Not an array?
+ auto arrayType = as<ArrayExpressionType>(varDecl->type);
+ if (!arrayType) return;
+
+ // Explicit element count given?
+ auto elementCount = arrayType->ArrayLength;
+ if (elementCount) return;
+
+ // No initializer?
+ auto initExpr = varDecl->initExpr;
+ if(!initExpr) return;
+
+ // Is the type of the initializer an array type?
+ if(auto arrayInitType = as<ArrayExpressionType>(initExpr->type))
+ {
+ elementCount = arrayInitType->ArrayLength;
+ }
+ else
+ {
+ // Nothing to do: we couldn't infer a size
+ return;
+ }
+
+ // Create a new array type based on the size we found,
+ // and install it into our type.
+ varDecl->type.type = getArrayType(
+ arrayType->baseType,
+ elementCount);
+ }
+
+ void SemanticsVisitor::validateArraySizeForVariable(VarDeclBase* varDecl)
+ {
+ auto arrayType = as<ArrayExpressionType>(varDecl->type);
+ if (!arrayType) return;
+
+ auto elementCount = arrayType->ArrayLength;
+ if (!elementCount)
+ {
+ // Note(tfoley): For now we allow arrays of unspecified size
+ // everywhere, because some source languages (e.g., GLSL)
+ // allow them in specific cases.
+#if 0
+ getSink()->diagnose(varDecl, Diagnostics::invalidArraySize);
+#endif
+ return;
+ }
+
+ // TODO(tfoley): How to handle the case where bound isn't known?
+ if (GetMinBound(elementCount) <= 0)
+ {
+ getSink()->diagnose(varDecl, Diagnostics::invalidArraySize);
+ return;
+ }
+ }
+
+ void SemanticsVisitor::visitVarDecl(VarDecl* varDecl)
+ {
+ CheckVarDeclCommon(varDecl);
+ }
+
+ void SemanticsVisitor::registerExtension(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))
+ {
+ // Attach our extension to that type as a candidate...
+ if (auto aggTypeDeclRef = targetDeclRefType->declRef.as<AggTypeDecl>())
+ {
+ auto aggTypeDecl = aggTypeDeclRef.getDecl();
+ decl->nextCandidateExtension = aggTypeDecl->candidateExtensions;
+ aggTypeDecl->candidateExtensions = decl;
+ return;
+ }
+ }
+ 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,
+ // but if the declaration is generic, the parent will be
+ // the `GenericDecl` and we need to skip past that to
+ // the grandparent.
+ //
+ auto parent = decl->ParentDecl;
+ auto genericParent = as<GenericDecl>(parent);
+ if (genericParent)
+ {
+ parent = genericParent->ParentDecl;
+ }
+
+ // Now look at the type of the parent (or grandparent).
+ if (auto aggTypeDecl = as<AggTypeDecl>(parent))
+ {
+ // We are nested in an aggregate type declaration,
+ // so the result type of the initializer will just
+ // be the surrounding type.
+ return DeclRefType::Create(
+ getSession(),
+ makeDeclRef(aggTypeDecl));
+ }
+ else if (auto extDecl = as<ExtensionDecl>(parent))
+ {
+ // We are nested inside an extension, so the result
+ // type needs to be the type being extended.
+ return extDecl->targetType.type;
+ }
+ else
+ {
+ getSink()->diagnose(decl, Diagnostics::initializerNotInsideType);
+ return nullptr;
+ }
+ }
+
+ void SemanticsVisitor::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
+ {
+ // TODO(tfoley): check body
+ }
+ decl->SetCheckState(getCheckedState());
+ }
+
+ void SemanticsVisitor::visitSubscriptDecl(SubscriptDecl* decl)
+ {
+ if (decl->IsChecked(getCheckedState())) return;
+ for (auto& paramDecl : decl->GetParameters())
+ {
+ paramDecl->type = CheckUsableType(paramDecl->type);
+ }
+
+ decl->ReturnType = CheckUsableType(decl->ReturnType);
+
+ // If we have a subscript declaration with no accessor declarations,
+ // then we should create a single `GetterDecl` to represent
+ // the implicit meaning of their declaration, so:
+ //
+ // subscript(uint index) -> T;
+ //
+ // becomes:
+ //
+ // subscript(uint index) -> T { get; }
+ //
+
+ bool anyAccessors = false;
+ for(auto accessorDecl : decl->getMembersOfType<AccessorDecl>())
+ {
+ anyAccessors = true;
+ }
+
+ if(!anyAccessors)
+ {
+ RefPtr<GetterDecl> getterDecl = new GetterDecl();
+ getterDecl->loc = decl->loc;
+
+ getterDecl->ParentDecl = decl;
+ decl->Members.add(getterDecl);
+ }
+
+ for(auto mm : decl->Members)
+ {
+ checkDecl(mm);
+ }
+
+ decl->SetCheckState(getCheckedState());
+ }
+
+ void SemanticsVisitor::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))
+ {
+ decl->ReturnType = parentSubscript->ReturnType;
+ }
+ // TODO: when we add "property" declarations, check for them here
+ else
+ {
+ getSink()->diagnose(decl, Diagnostics::accessorMustBeInsideSubscriptOrProperty);
+ }
+
+ }
+ else
+ {
+ // TODO: check the body!
+ }
+ decl->SetCheckState(getCheckedState());
+ }
+
+ GenericDecl* SemanticsVisitor::GetOuterGeneric(Decl* decl)
+ {
+ auto parentDecl = decl->ParentDecl;
+ if (!parentDecl) return nullptr;
+ auto parentGeneric = as<GenericDecl>(parentDecl);
+ return parentGeneric;
+ }
+
+ DeclRef<ExtensionDecl> SemanticsVisitor::ApplyExtensionToType(
+ ExtensionDecl* extDecl,
+ RefPtr<Type> type)
+ {
+ DeclRef<ExtensionDecl> extDeclRef = makeDeclRef(extDecl);
+
+ // If the extension is a generic extension, then we
+ // need to infer type arguments that will give
+ // us a target type that matches `type`.
+ //
+ if (auto extGenericDecl = GetOuterGeneric(extDecl))
+ {
+ ConstraintSystem constraints;
+ constraints.loc = extDecl->loc;
+ constraints.genericDecl = extGenericDecl;
+
+ if (!TryUnifyTypes(constraints, extDecl->targetType.Ptr(), type))
+ return DeclRef<ExtensionDecl>();
+
+ auto constraintSubst = TrySolveConstraintSystem(&constraints, DeclRef<Decl>(extGenericDecl, nullptr).as<GenericDecl>());
+ if (!constraintSubst)
+ {
+ return DeclRef<ExtensionDecl>();
+ }
+
+ // Construct a reference to the extension with our constraint variables
+ // set as they were found by solving the constraint system.
+ extDeclRef = DeclRef<Decl>(extDecl, constraintSubst).as<ExtensionDecl>();
+ }
+
+ // Now extract the target type from our (possibly specialized) extension decl-ref.
+ RefPtr<Type> targetType = GetTargetType(extDeclRef);
+
+ // As a bit of a kludge here, if the target type of the extension is
+ // an interface, and the `type` we are trying to match up has a this-type
+ // substitution for that interface, then we want to attach a matching
+ // substitution to the extension decl-ref.
+ if(auto targetDeclRefType = as<DeclRefType>(targetType))
+ {
+ if(auto targetInterfaceDeclRef = targetDeclRefType->declRef.as<InterfaceDecl>())
+ {
+ // Okay, the target type is an interface.
+ //
+ // Is the type we want to apply to also an interface?
+ if(auto appDeclRefType = as<DeclRefType>(type))
+ {
+ if(auto appInterfaceDeclRef = appDeclRefType->declRef.as<InterfaceDecl>())
+ {
+ if(appInterfaceDeclRef.getDecl() == targetInterfaceDeclRef.getDecl())
+ {
+ // Looks like we have a match in the types,
+ // now let's see if we have a this-type substitution.
+ if(auto appThisTypeSubst = appInterfaceDeclRef.substitutions.substitutions.as<ThisTypeSubstitution>())
+ {
+ if(appThisTypeSubst->interfaceDecl == appInterfaceDeclRef.getDecl())
+ {
+ // The type we want to apply to has a this-type substitution,
+ // and (by construction) the target type currently does not.
+ //
+ SLANG_ASSERT(!targetInterfaceDeclRef.substitutions.substitutions.as<ThisTypeSubstitution>());
+
+ // We will create a new substitution to apply to the target type.
+ RefPtr<ThisTypeSubstitution> newTargetSubst = new ThisTypeSubstitution();
+ newTargetSubst->interfaceDecl = appThisTypeSubst->interfaceDecl;
+ newTargetSubst->witness = appThisTypeSubst->witness;
+ newTargetSubst->outer = targetInterfaceDeclRef.substitutions.substitutions;
+
+ targetType = DeclRefType::Create(getSession(),
+ DeclRef<InterfaceDecl>(targetInterfaceDeclRef.getDecl(), newTargetSubst));
+
+ // Note: we are constructing a this-type substitution that
+ // we will apply to the extension declaration as well.
+ // This is not strictly allowed by our current representation
+ // choices, but we need it in order to make sure that
+ // references to the target type of the extension
+ // declaration have a chance to resolve the way we want them to.
+
+ RefPtr<ThisTypeSubstitution> newExtSubst = new ThisTypeSubstitution();
+ newExtSubst->interfaceDecl = appThisTypeSubst->interfaceDecl;
+ newExtSubst->witness = appThisTypeSubst->witness;
+ newExtSubst->outer = extDeclRef.substitutions.substitutions;
+
+ extDeclRef = DeclRef<ExtensionDecl>(
+ extDeclRef.getDecl(),
+ newExtSubst);
+
+ // TODO: Ideally we should also apply the chosen specialization to
+ // the decl-ref for the extension, so that subsequent lookup through
+ // the members of this extension will retain that substitution and
+ // be able to apply it.
+ //
+ // E.g., if an extension method returns a value of an associated
+ // type, then we'd want that to become specialized to a concrete
+ // type when using the extension method on a value of concrete type.
+ //
+ // The challenge here that makes me reluctant to just staple on
+ // such a substitution is that it wouldn't follow our implicit
+ // rules about where `ThisTypeSubstitution`s can appear.
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // In order for this extension to apply to the given type, we
+ // need to have a match on the target types.
+ if (!type->Equals(targetType))
+ return DeclRef<ExtensionDecl>();
+
+
+ return extDeclRef;
+ }
+
+ QualType SemanticsVisitor::GetTypeForDeclRef(DeclRef<Decl> declRef)
+ {
+ return getTypeForDeclRef(
+ getSession(),
+ this,
+ getSink(),
+ declRef,
+ &typeResult);
+ }
+
+ void SemanticsVisitor::importModuleIntoScope(Scope* scope, ModuleDecl* moduleDecl)
+ {
+ // If we've imported this one already, then
+ // skip the step where we modify the current scope.
+ if (importedModules.Contains(moduleDecl))
+ {
+ return;
+ }
+ importedModules.Add(moduleDecl);
+
+
+ // Create a new sub-scope to wire the module
+ // into our lookup chain.
+ auto subScope = new Scope();
+ subScope->containerDecl = moduleDecl;
+
+ subScope->nextSibling = scope->nextSibling;
+ scope->nextSibling = subScope;
+
+ // Also import any modules from nested `import` declarations
+ // with the `__exported` modifier
+ for (auto importDecl : moduleDecl->getMembersOfType<ImportDecl>())
+ {
+ if (!importDecl->HasModifier<ExportedModifier>())
+ continue;
+
+ importModuleIntoScope(scope, importDecl->importedModuleDecl.Ptr());
+ }
+ }
+
+ void SemanticsVisitor::visitEmptyDecl(EmptyDecl* /*decl*/)
+ {
+ // nothing to do
+ }
+
+ void SemanticsVisitor::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
+ // the current scope.
+
+ auto name = decl->moduleNameAndLoc.name;
+ auto scope = decl->scope;
+
+ // Try to load a module matching the name
+ auto importedModule = findOrImportModule(
+ getLinkage(),
+ name,
+ decl->moduleNameAndLoc.loc,
+ getSink());
+
+ // If we didn't find a matching module, then bail out
+ if (!importedModule)
+ return;
+
+ // Record the module that was imported, so that we can use
+ // it later during code generation.
+ auto importedModuleDecl = importedModule->getModuleDecl();
+ decl->importedModuleDecl = importedModuleDecl;
+
+ // Add the declarations from the imported module into the scope
+ // that the `import` declaration is set to extend.
+ //
+ importModuleIntoScope(scope.Ptr(), importedModuleDecl);
+
+ // Record the `import`ed module (and everything it depends on)
+ // as a dependency of the module we are compiling.
+ if(auto module = getModule(decl))
+ {
+ module->addModuleDependency(importedModule);
+ }
+
+ decl->SetCheckState(getCheckedState());
+ }
+
+}