// slang-check-type.cpp #include "slang-check-impl.h" // This file implements semantic checking logic related to types // and type expressions (aka `TypeRepr`). namespace Slang { RefPtr checkProperType( Linkage* linkage, TypeExp typeExp, DiagnosticSink* sink) { SharedSemanticsContext sharedSemanticsContext( linkage, sink); SemanticsVisitor visitor(&sharedSemanticsContext); auto typeOut = visitor.CheckProperType(typeExp); return typeOut.type; } RefPtr SemanticsVisitor::TranslateTypeNodeImpl(const RefPtr & node) { if (!node) return nullptr; auto expr = CheckTerm(node); expr = ExpectATypeRepr(expr); return expr; } RefPtr SemanticsVisitor::ExtractTypeFromTypeRepr(const RefPtr& typeRepr) { if (!typeRepr) return nullptr; if (auto typeType = as(typeRepr->type)) { return typeType->type; } return getSession()->getErrorType(); } RefPtr SemanticsVisitor::TranslateTypeNode(const RefPtr & node) { if (!node) return nullptr; auto typeRepr = TranslateTypeNodeImpl(node); return ExtractTypeFromTypeRepr(typeRepr); } TypeExp SemanticsVisitor::TranslateTypeNodeForced(TypeExp const& typeExp) { auto typeRepr = TranslateTypeNodeImpl(typeExp.exp); TypeExp result; result.exp = typeRepr; result.type = ExtractTypeFromTypeRepr(typeRepr); return result; } TypeExp SemanticsVisitor::TranslateTypeNode(TypeExp const& typeExp) { // HACK(tfoley): It seems that in some cases we end up re-checking // syntax that we've already checked. We need to root-cause that // issue, but for now a quick fix in this case is to early // exist if we've already got a type associated here: if (typeExp.type) { return typeExp; } return TranslateTypeNodeForced(typeExp); } RefPtr SemanticsVisitor::ExpectATypeRepr(RefPtr expr) { if (auto overloadedExpr = as(expr)) { expr = resolveOverloadedExpr(overloadedExpr, LookupMask::type); } if (auto typeType = as(expr->type)) { return expr; } else if (auto errorType = as(expr->type)) { return expr; } getSink()->diagnose(expr, Diagnostics::unimplemented, "expected a type"); return CreateErrorExpr(expr); } RefPtr SemanticsVisitor::ExpectAType(RefPtr expr) { auto typeRepr = ExpectATypeRepr(expr); if (auto typeType = as(typeRepr->type)) { return typeType->type; } return getSession()->getErrorType(); } RefPtr SemanticsVisitor::ExtractGenericArgType(RefPtr exp) { return ExpectAType(exp); } RefPtr SemanticsVisitor::ExtractGenericArgInteger(RefPtr exp) { return CheckIntegerConstantExpression(exp.Ptr()); } RefPtr SemanticsVisitor::ExtractGenericArgVal(RefPtr exp) { if (auto overloadedExpr = as(exp)) { // assume that if it is overloaded, we want a type exp = resolveOverloadedExpr(overloadedExpr, LookupMask::type); } if (auto typeType = as(exp->type)) { return typeType->type; } else if (auto errorType = as(exp->type)) { return exp->type.type; } else { return ExtractGenericArgInteger(exp); } } RefPtr SemanticsVisitor::InstantiateGenericType( DeclRef genericDeclRef, List> const& args) { RefPtr subst = new GenericSubstitution(); subst->genericDecl = genericDeclRef.getDecl(); subst->outer = genericDeclRef.substitutions.substitutions; for (auto argExpr : args) { subst->args.add(ExtractGenericArgVal(argExpr)); } DeclRef innerDeclRef; innerDeclRef.decl = GetInner(genericDeclRef); innerDeclRef.substitutions = SubstitutionSet(subst); return DeclRefType::Create( getSession(), innerDeclRef); } bool SemanticsVisitor::CoerceToProperTypeImpl( TypeExp const& typeExp, RefPtr* outProperType, DiagnosticSink* diagSink) { Type* type = typeExp.type.Ptr(); if(!type && typeExp.exp) { auto expr = typeExp.exp; expr = maybeResolveOverloadedExpr(expr, LookupMask::type, diagSink); if(auto typeType = as(expr->type)) { type = typeType->type; } } if (!type) { if (outProperType) { *outProperType = nullptr; } return false; } if (auto genericDeclRefType = as(type)) { // We are using a reference to a generic declaration as a concrete // type. This means we should substitute in any default parameter values // if they are available. // // TODO(tfoley): A more expressive type system would substitute in // "fresh" variables and then solve for their values... // auto genericDeclRef = genericDeclRefType->GetDeclRef(); ensureDecl(genericDeclRef, DeclCheckState::CanSpecializeGeneric); List> args; for (RefPtr member : genericDeclRef.getDecl()->Members) { if (auto typeParam = as(member)) { if (!typeParam->initType.exp) { if (diagSink) { diagSink->diagnose(typeExp.exp.Ptr(), Diagnostics::genericTypeNeedsArgs, typeExp); *outProperType = getSession()->getErrorType(); } return false; } // TODO: this is one place where syntax should get cloned! if (outProperType) args.add(typeParam->initType.exp); } else if (auto valParam = as(member)) { if (!valParam->initExpr) { if (diagSink) { diagSink->diagnose(typeExp.exp.Ptr(), Diagnostics::unimplemented, "can't fill in default for generic type parameter"); *outProperType = getSession()->getErrorType(); } return false; } // TODO: this is one place where syntax should get cloned! if (outProperType) args.add(valParam->initExpr); } else { // ignore non-parameter members } } if (outProperType) { *outProperType = InstantiateGenericType(genericDeclRef, args); } return true; } // default case: we expect this to already be a proper type if (outProperType) { *outProperType = type; } return true; } TypeExp SemanticsVisitor::CoerceToProperType(TypeExp const& typeExp) { TypeExp result = typeExp; CoerceToProperTypeImpl(typeExp, &result.type, getSink()); return result; } TypeExp SemanticsVisitor::tryCoerceToProperType(TypeExp const& typeExp) { TypeExp result = typeExp; if(!CoerceToProperTypeImpl(typeExp, &result.type, nullptr)) return TypeExp(); return result; } TypeExp SemanticsVisitor::CheckProperType(TypeExp typeExp) { return CoerceToProperType(TranslateTypeNode(typeExp)); } TypeExp SemanticsVisitor::CoerceToUsableType(TypeExp const& typeExp) { TypeExp result = CoerceToProperType(typeExp); Type* type = result.type.Ptr(); if (auto basicType = as(type)) { // TODO: `void` shouldn't be a basic type, to make this easier to avoid if (basicType->baseType == BaseType::Void) { // TODO(tfoley): pick the right diagnostic message getSink()->diagnose(result.exp.Ptr(), Diagnostics::invalidTypeVoid); result.type = getSession()->getErrorType(); return result; } } return result; } TypeExp SemanticsVisitor::CheckUsableType(TypeExp typeExp) { return CoerceToUsableType(TranslateTypeNode(typeExp)); } bool SemanticsVisitor::ValuesAreEqual( RefPtr left, RefPtr right) { if(left == right) return true; if(auto leftConst = as(left)) { if(auto rightConst = as(right)) { return leftConst->value == rightConst->value; } } if(auto leftVar = as(left)) { if(auto rightVar = as(right)) { return leftVar->declRef.Equals(rightVar->declRef); } } return false; } RefPtr SemanticsVisitor::createVectorType( RefPtr elementType, RefPtr elementCount) { auto session = getSession(); auto vectorGenericDecl = findMagicDecl( session, "Vector").as(); auto vectorTypeDecl = vectorGenericDecl->inner; auto substitutions = new GenericSubstitution(); substitutions->genericDecl = vectorGenericDecl.Ptr(); substitutions->args.add(elementType); substitutions->args.add(elementCount); auto declRef = DeclRef(vectorTypeDecl.Ptr(), substitutions); return DeclRefType::Create( session, declRef).as(); } RefPtr SemanticsExprVisitor::visitSharedTypeExpr(SharedTypeExpr* expr) { if (!expr->type.Ptr()) { expr->base = CheckProperType(expr->base); expr->type = expr->base.exp->type; } return expr; } RefPtr SemanticsExprVisitor::visitTaggedUnionTypeExpr(TaggedUnionTypeExpr* expr) { // We have an expression of the form `__TaggedUnion(A, B, ...)` // which will evaluate to a tagged-union type over `A`, `B`, etc. // RefPtr type = new TaggedUnionType(); expr->type = QualType(getTypeType(type)); for( auto& caseTypeExpr : expr->caseTypes ) { caseTypeExpr = CheckProperType(caseTypeExpr); type->caseTypes.add(caseTypeExpr.type); } return expr; } }